c++17带来的代码变化 https://github.com/tvaneerd/cpp17_in_TTs/blob/master/ALL_IN_ONE.md Descriptions of C++17 features, presented mostly in "Tony Tables" (hey, the tables were my idea, but their name wasn't :-). There are actually **over 100 changes in C++17**, only some of them are listed here. Caveat1: At time of writing, *C++17 was completed, but not signed off yet.* There may be slight differences, although _highly_ unlikely (modulo defects). Caveat2: *I make mistakes.* This is more likely :-) Created with the support of my employer, [Christie Digital Systems](https://www.christiedigital.com/en-us/business/solutions/projection-mapping). --- if-init ---
| C++ | 
|---|
{
   if (Foo * ptr = get_foo())
      use(*ptr);
   more_code();
}
 | 
| C++ | 
|---|
{
   {
      QVariant var = getAnswer();
      if (var.isValid())
         use(var);
   }
   more_code();
}
 | 
| C++ | C++17 | 
|---|---|
{
   {
      QVariant var = getAnswer();
      if (var.isValid())
         use(var);
   }
   more_code();
}
 | 
{
   
   
   if (QVariant var = getAnswer(); var.isValid())
      use(var);
      
   more_code();
}
 | 
| C++17 | 
|---|
{
   switch (Device dev = get_device(); dev.state())
   {
   case sleep: /*...*/ break;
   case ready: /*...*/ break;
   case bad: /*...*/ break;
   }
}
 | 
| C++14 | C++17 | |
|---|---|---|
   tuple<int, string> func();
   
   auto tup = func();
   int i = get<0>(tup);
   string s = get<1>(tup);
  
   use(s, ++i);
 | 
   tuple<int, string> func();
   
   int i;
   string s;
   std::tie(i,s) = func();
   use(s, ++i);
 | 
   tuple<int, string> func();
   
   
   auto [ i, s ] = func();
   use(s, ++i);
 | 
| C++17 | compiler | 
|---|---|
   pair<int, string> func();
   
   
   auto [ i, s ] = func();
   use(s, ++i);
 | 
   pair<int, string> func();
   
   auto __tmp = func();
   auto & i = get<0>(__tmp);
   auto & s = get<1>(__tmp);
   use(s, ++i);
 | 
| C++17 | compiler | 
|---|---|
#include <string>
#include <iostream>
struct Foo
{
   int x = 0;
   std::string str = "world";
   ~Foo() { std::cout << str; }
};
int main()
{
    auto [ i, s ] = Foo();
    std::cout << "hello ";
    s = "structured bindings";
}
 | 
#include <string>
#include <iostream>
struct Foo
{
   int x = 0;
   std::string str = "world";
   ~Foo() { std::cout << str; }
};
int main()
{
    auto __tmp = Foo();
    std::cout << "hello ";
    __tmp.str = "structured bindings";
}
 | 
Output | 
| hello structured bindings | |
| C++17 | compiler | 
|---|---|
   struct X { int i = 0; };
   X makeX();
   
   X x;
   
   auto [ b ] = makeX();
   b++;
   auto const [ c ] = makeX();
   c++;
   auto & [ d ] = makeX();
   d++;
   auto & [ e ] = x;
   e++;
   auto const & [ f ] = makeX();
   f++;
 | 
   struct X { int i = 0; };
   X makeX();
   
   X x;
   
   auto __tmp1 = makeX();
   __tmp1.i++;
   auto const __tmp2 = makeX();
   __tmp2.i++; //error: can't modify const
   auto & __tmp3 = makeX(); //error: non-const ref cannot bind to temp
   
   auto & _tmp3 = x;
   x.i++;
   auto const & _tmp4 = makeX();
   __tmp4.i++; //error: can't modify const
 | 
| C++17 | compiler | 
|---|---|
   struct Foo {
      int x;
      string str;
   };
   
   Foo func();
     
     
   auto [ i, s ] = func();
   use(s, ++i);
 | 
   struct Foo {
      int x;
      string str;
   };
   
   Foo func();
   
   Foo __tmp = func();
   auto & i = __tmp.x;
   auto & s = __tmp.str;
   use(s, ++i);
 | 
| C++17 | 
|---|
   class Foo {
      // ...
   public:
      template <int N> auto & get() /*const?*/ { /*...*/ }
   };
   // or get outside class
   template<int N> auto & get(Foo /*const?*/ & foo) { /*...*/ }
   //...
   
   // tuple_size/element specialized
   // yes, in namespace std
   namespace std {
      // how many elements does Foo have
      template<> struct tuple_size<Foo> { static const int value = 3; }
      // what type is element N
      template<int N> struct tuple_element<N, Foo> { using type = ...add code here...; }
   }
   
   Foo func();
   auto [ i, s ] = func();
   use(s, ++i);
 | 
| etc | 
|---|
    
   int arr[4] = { /*...*/ };
   auto [ a, b, c, d ] = arr; 
   auto [ t, u, v ] = std::array<int,3>();
   
   // now we're talkin'
   for (auto && [key, value] : my_map)
   {
      //...
   }
 | 
| C++14 | C++17 | 
|---|---|
class Foo {
  int myInt;
  string myString;
public:
  int const & refInt() const
  { return myInt; }
  string const & refString() const
  { return myString; }
};
namespace std
{
   template<> class tuple_size<Foo>
       : public integral_constant<int, 2>
   { };
   template<int N> class tuple_element<N, Foo>
   {
   public:
      using type =
      conditional_t<N==0,int const &,string const &>;
   };
}
template<int N> std::tuple_element_t<N,Foo>
get(Foo const &);
// here's some specializations (the real stuff)
template<> std::tuple_element_t<0,Foo>
get<0>(Foo const & foo)
{
  return foo.refInt();
}
template<> std::tuple_element_t<1,Foo>
get<1>(Foo const & foo)
{
  return foo.refString();
}
 | 
class Foo {
  int myInt;
  string myString;
public:
  int const & refInt() const
  { return myInt; }
  string const & refString() const
  { return myString; }
};
namespace std
{
   template<> class tuple_size<Foo>
       : public integral_constant<int, 2>
   { };
   template<int N> class tuple_element<N, Foo>
   {
   public:
      using type =
      conditional_t<N==0,int const &,string const &>;
   };
}
template<int N> auto & get(Foo const & foo)
{
  static_assert(0 <= N && N < 2, "Foo only has 2 members");
  if constexpr (N == 0)  // !! LOOK HERE !!
     return foo.refInt();
  else if constexpr (N == 1)    // !! LOOK HERE !!
     return foo.refString();
}
 | 
| C++14 | C++17 | 
|---|---|
pair<int, string> is1 = pair<int, string>(17, "hello");
auto is2 = std::pair<int, string>(17, "hello");
auto is3 = std::make_pair(17, string("hello"));
auto is4 = std::make_pair(17, "hello"s);
 | 
pair<int, string> is1 = pair(17, "hello");
auto is2 = pair(17, "hello"); // !! pair<int, char const *>
auto is3 = pair(17, string("hello"));
auto is4 = pair(17, "hello"s);
 | 
template<typename T>
struct Thingy
{
  T t;
};
// !! LOOK HERE !!
Thingy(const char *) -> Thingy<std::string>;
Thingy thing{"A String"}; // thing.t is a `std::string`.
_(example from "Nicol Bolas")_
#### Implicit Deduction Guides
For any `template| C++14 | C++17 | 
|---|---|
template <typename T, T v>
struct integral_constant
{
   static constexpr T value = v;
};
integral_constant<int, 2048>::value
integral_constant<char, 'a'>::value
 | 
template <auto v>
struct integral_constant
{
   static constexpr auto value = v;
};
integral_constant<2048>::value
integral_constant<'a'>::value
 | 
| How do you write `sum()` ? | 
|---|
auto x = sum(5, 8);
auto y = sum(a, b, 17, 3.14, etc);
 | 
| C++14 | C++17 | 
|---|---|
auto sum() { return 0; }
template <typename T>
auto sum(T&& t) { return t; }
template <typename T, typename... Rest>
auto sum(T&& t, Rest&&... r) {
   return t + sum(std::forward<Rest>(r)...);
}
 | 
template <typename... Args>
auto sum(Args&&... args) {
   return (args + ... + 0);
}
 | 
| C++14 | C++17 | 
|---|---|
   namespace A {
      namespace B {
         namespace C {
            struct Foo { };
            //...
         }
      }
   }
 | 
   namespace A::B::C {
      struct Foo { };
      //...
   }
 | 
| C++14 | C++17 | 
|---|---|
static_assert(sizeof(short) == 2, "sizeof(short) == 2")
 | 
static_assert(sizeof(short) == 2)
 | 
| Output | |
| static assertion failure: sizeof(short) == 2 | |
| C++14 | C++17 | 
|---|---|
// foo.h
extern int foo;
// foo.cpp
int foo = 10;
 | 
// foo.h
inline int foo = 10;
 | 
| C++14 | C++17 | 
|---|---|
// foo.h
struct Foo {
   static int foo;
};
// foo.cpp
int Foo::foo = 10;
 | 
// foo.h
struct Foo {
   static inline int foo = 10;
};
 | 
| C++17 | 
|---|
// header <mutex>
namespace std
{
   template <typename M>
   struct lock_guard
   {
      explicit lock_guard(M & mutex);
      // not copyable, not movable:
      lock_guard(lock_guard const & ) = delete;
      //...
   }
}
// your code
lock_guard<mutex> grab_lock(mutex & mtx)
{
   return lock_guard<mutex>(mtx);
}
mutex mtx;
void foo()
{
   auto guard = grab_lock(mtx);
   /* do stuff holding lock */
}
 | 
| C++14 | C++17 | 
|---|---|
switch (device.status())
{
case sleep:
   device.wake();
   // fall thru
case ready:
   device.run();
   break;
case bad:
   handle_error();
   break;
}
 | 
switch (device.status())
{
case sleep:
   device.wake();
   [[fallthrough]];
case ready:
   device.run();
   break;
case bad:
   handle_error();
   break;
}
 | 
| Compiler | Compiler | 
| warning: case statement without break | 
| C++14 | C++17 | 
|---|---|
struct SomeInts
{
   bool empty();
   void push_back(int);
   //etc
};
void random_fill(SomeInts & container,
      int min, int max, int count)
{
   container.empty(); // empty it first
   for (int num : gen_rand(min, max, count))
      container.push_back(num);
}
 | 
struct SomeInts
{
   [[nodiscard]] bool empty();
   void push_back(int);
   //etc
};
void random_fill(SomeInts & container,
      int min, int max, int count)
{
   container.empty(); // empty it first
   for (int num : gen_rand(min, max, count))
      container.push_back(num);
}
 | 
| Compiler | C++17 Compiler | 
| warning: ignoring return value of 'bool empty()' | 
| C++14 | C++17 | 
|---|---|
struct MyError {
  std::string message;
  int code;
};
MyError divide(int a, int b) {
  if (b == 0) {
    return {"Division by zero", -1};
  }
  std::cout << (a / b) << '\n';
  return {};
}
divide(1, 2);
 | 
struct [[nodiscard]] MyError {
  std::string message;
  int code;
};
MyError divide(int a, int b) {
  if (b == 0) {
    return {"Division by zero", -1};
  }
  std::cout << (a / b) << '\n';
  return {};
}
divide(1, 2);
 | 
| Compiler | C++17 Compiler | 
| warning: ignoring return value of function declared with 'nodiscard' attribute | 
| C++14 | C++17 | 
|---|---|
   bool res = step1();
   assert(res);
   step2();
   etc();
 | 
   [[maybe_unused]] bool res = step1();
   assert(res);
   step2();
   etc();
 | 
| Compiler | C++17 Compiler | 
| warning: unused variable 'res' | 
| C++17 | 
|---|
   [[maybe_unused]] void f()
   {
      /*...*/
   }
   int main()
   {
   }
 | 
| C++14 | C++17 | 
|---|---|
Foo parseFoo(std::string const & input);
Foo parseFoo(char const * str);
Foo parseFoo(char const * str, int length);
Foo parseFoo(MyString const & str);
 | 
Foo parseFoo(std::string_view input);
// I would say don't offer this interface, but:
Foo parseFoo(char const * str, int length)
{
   return parseFoo(string_view(str,length));
}
class MyString {
   //...
   operator string_view() const
   {
      return string_view(this->data, this->length);
   }
};
 | 
| C++14 | C++17 | 
|---|---|
// returns default Foo on error
Foo parseFoo(std::string_view in);
// throws parse_error
Foo parseFoo(std::string_view in);
// returns false on error
bool parseFoo(std::string_view in, Foo & output);
// returns null on error
unique_ptr<Foo> parseFoo(std::string_view in);
 | 
std::optional<Foo> parseFoo(std::string_view in);
 | 
| C++17 | 
|---|
optional ofoo = parseFoo(str);
if (ofoo)
   use(*ofoo);
 | 
// nicer with new if syntax:
if (optional ofoo = parseFoo(str); ofoo)
   use(*ofoo);
 | 
optional<int> oi = parseInt(str);
std::cout << oi.value_or(0);
 | 
| C++14 | C++17 | 
|---|---|
struct Stuff
{
   union Data {
      int i;
      double d;
      string s;  // constructor/destructor???
   } data;
   enum Type { INT, DOUBLE, STRING } type;
};
 | 
struct Stuff
{
   std::variant<int, double, string> data;
};
 | 
| C++14 | C++17 | 
|---|---|
void handleData(int i);
void handleData(double d);
void handleData(string const & s);
//...
switch (stuff.type)
{
case INT:
   handleData(stuff.data.i);
   break;
case DOUBLE:
   handleData(stuff.data.d);
   break;
case STRING:
   handleData(stuff.data.s);
   break;
}
 | 
void handleData(int i);
void handleData(double d);
void handleData(string const & s);
//...
std::visit([](auto const & val) { handleData(val); }, stuff.data);
// can also switch(stuff.data.index())
 | 
| How the above lambda works | 
|---|
struct ThatLambda
{
   void operator()(int const & i) { handleData(i); }
   void operator()(double const & d) { handleData(d); }
   void operator()(string const & s) { handleData(s); }
};
ThatLambda thatLambda;
std::visit(thatLambda, stuff.data);
 | 
| C++17 | 
|---|
if (holds_alternative<int>(data))
   int i = get<int>(data);
// throws if not double:
double d = get<double>(data);
 | 
| C++17 | 
|---|
std::variant<Foo, Bar> var;  // calls Foo()
// (or doesn't compile if no Foo())
Bar bar = makeBar();
var = bar; // calls ~Foo() and Bar(Bar const &)
// (what if Bar(Bar const & b) throws?)
var = Foo(); // calls ~Bar() and move-ctor Foo(Foo &&)
// (what if Foo(Foo && b) throws? - even though moves shouldn't throw)
var = someFoo;  // calls Foo::operator=(Foo const &)
std::variant<Foo, std::string> foostr;
foostr = "hello"; // char * isn't Foo or string
// yet foostr holds a std::string
 | 
| C++14 | C++17 | C++17 | 
|---|---|---|
void * v = ...;
if (v != nullptr) {
   // hope and pray it is an int:
   int i = *reinterpret_cast<int*>(v);
}
 | 
std::any v = ...;
if (v.has_value()) {
   // throws if not int
   int i = any_cast<int>(v);
}
 | 
std::any v = ...;
if (v.type() == typeid(int)) {
   // definitely an int
   int i = any_cast<int>(v);
}
 | 
| C++14 | C++17 | 
|---|---|
// can hold Circles, Squares, Triangles,...
std::vector<Shape *> shapes;
 | 
// can hold Circles, Squares, Triangles, ints, strings,...
std::vector<any> things;
 | 
| C++14 Windows | C++17 | 
|---|---|
#include <windows.h>
void copy_foobar() {
  std::wstring dir = L"\\sandbox";
  std::wstring p = dir + L"\\foobar.txt";
  std::wstring copy = p;
  copy += ".bak";
  CopyFile(p, copy, false);
  
  std::string dir_copy = dir + ".bak";
  SHFILEOPSTRUCT s = { 0 };
  s.hwnd = someHwndFromSomewhere;
  s.wFunc = FO_COPY;
  s.fFlags = FOF_SILENT;
  s.pFrom = dir.c_str();
  s.pTo = dir_copy.c_str();
  SHFileOperation(&s);
}
void display_contents(std::wstring const & p) {
  std::cout << p << "\n";
  std::wstring search = p + "\\*";
  WIN32_FIND_DATA ffd;
  HANDLE hFind =
        FindFirstFile(search.c_str(), &ffd);
  if (hFind == INVALID_HANDLE_VALUE)
     return;
  
  do {
    if ( ffd.dwFileAttributes
         & FILE_ATTRIBUTE_DIRECTORY) {
      std::cout << "  " << ffd.cFileName << "\n";
    } else {
      LARGE_INTEGER filesize;
      filesize.LowPart = ffd.nFileSizeLow;
      filesize.HighPart = ffd.nFileSizeHigh;
      std::cout << "  " << ffd.cFileName
                << " [" << filesize.QuadPart
                << " bytes]\n";
    }
  } while (FindNextFile(hFind, &ffd) != 0);
}
 | 
#include <filesystem>
#include <iostream>
namespace fs = std::filesystem;
void copy_foobar() {
  fs::path dir = "/";
  dir /= "sandbox";
  fs::path p = dir / "foobar.txt";
  fs::path copy = p;
  copy += ".bak";
  fs::copy(p, copy);
  fs::path dir_copy = dir;
  dir_copy += ".bak";
  fs::copy(dir, dir_copy, fs::copy_options::recursive);
}
void display_contents(fs::path const & p) {
  std::cout << p.filename() << "\n";
  if (!fs::is_directory(p))
    return;
  for (auto const & e: fs::directory_iterator{p}) {
    if (fs::is_regular_file(e.status())) {
      std::cout << "  " << e.path().filename()
                << " [" << fs::file_size(e) << " bytes]\n";
    } else if (fs::is_directory(e.status())) {
      std::cout << "  " << e.path().filename() << "\n";
    }
  }
}
 | 
| C++14 POSIX | |
#include <dirent.h>
#include <sys/stat.h>
#include <sys/types.h>
void copy_foobar() {
// [TODO]
// to copy file, use fread / fwrite
// how to copy directory...?
}
void display_contents(std::string const & p) {
  std::cout << p << "\n";
  struct dirent *dp;
  DIR *dfd;
  
  if ((dfd = opendir(p.c_str()) == nullptr)
    return;
  while((dp = readdir(dfd)) != nullptr) {
    struct stat st;
    string filename = p + "/" + dp->d_Name;
    if (stat(filename.c_str(), &st) == -1)
      continue;
      
    if ((st.st_mode & S_IFMT) == S_IFDIR)
      std::cout << "  " << filename << "\n";
    } else {
      std::cout << "  " << filename
                << " [" << st.st_size
                << " bytes]\n";
    }
  }
}
 | 
| C++14 | C++17 | 
|---|---|
std::for_each(first, last,
    [](auto & x){ process(x); }
);
std::copy(first, last, output);
std::sort(first, last);
std::transform(xfirst, xlast, yfirst,
    [=](double xi, double yi){ return a * xi + yi; }
);
 | 
std::for_each(std::execution::par, first, last,
    [](auto & x){ process(x); }
);
std::copy(std::execution::par, first, last, output);
std::sort(std::execution::par, first, last);
std::transform(std::execution::par_unseq, xfirst, xlast, yfirst,
    [=](double xi, double yi){ return a * xi + yi; }
);
 | 
| C++14 | Concepts TS | 
|---|---|
//
// T must be a Random Access Iterator
// otherwise you will either get the wrong answer,
// or, most likely, terrible compiler errors
//
template <typename T>
auto binarySearch(T first, T second)
{
   //...
}
 | 
template <RandomAccessIterator T>
auto binarySearch(T first, T second)
{
   //...
}
 | 
error: syntax error '[' unexpected error: gibberish error: more compiler gibberish error: for pages and pages ...  | 
error: MyIter does not model RandomAccessIterator  |