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();
}
But what do you do when it isn't (convertible to) boolean?
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();
}
Switch statements too!
C++17
{
   switch (Device dev = get_device(); dev.state())
   {
   case sleep: /*...*/ break;
   case ready: /*...*/ break;
   case bad: /*...*/ break;
   }
}
Structured Bindings -------------------
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);
Note, in the above, `__tmp` is a copy, but `i` and `s` are references. Or I should say "references" in quotes. Not exactly references, but real compiler synonyms for the members. (They are not real references as things like `decltype` "look through" the references to the actual members.) So even though `auto [i,s] = func();` has no `&` anywhere, there are still references involved. For example:
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
Note that the `s = "structured bindings";` is modifying `Foo::str` _inside_ of the temporary (hidden) `Foo`, so that when the temporary `Foo` is destroyed, its destructor prints `structured bindings` instead of `world`. So what _does_ a `&` do in a structured binding declaration? It gets applied to the hidden `__tmp` variable:
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
Wait, pair and tuple are not magic (just nearly impossible to write to STL quality), can *my* types work with this? **YES**. The compiler uses `get()` if available, or can work with plain structs directly: **Structs**
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);
**Implement your own get(), tuple_size, tuple_element** For any class/struct that doesn't work by default, you need to implement your own custom `get<>()` and you also need to implement `tuple_size` and `tuple_element`.
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);
**Arrays, std::array, etc, oh my!**
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)
   {
      //...
   }
Constexpr If ------------ Actually, how _would_ someone write a custom `get<>()` function for their class? (see [Structured Bindings](structured_bindings.md) for why you might want to do that) Since each `get<0>`, `get<1>`, etc returns a different member, which are possibly different types... (oh no, template metaprogramming...)
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();
}
P.S. `if constexpr (expression)` doesn't _check_ if the expression is constexpr. The expression _must_ be constexpr (else it doesn't compile). The part that is constexpr is 'doing' the if. Don't think about this and what syntax might be better. The committee argued about it long enough. Deduction Guides --- Speaking of pair and tuple...
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);
The magic behind the above is called "deduction guides". In particular, _implicit_ deduction guides, and _explicit_ deduction guides. #### Explicit Deduction Guides
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 struct`... (or class!) if there is a constructor that takes `T` and `U` such that it can figure out all the types, then that constructor forms an "implicit" deduction guide. ie just like the explicit one above, but the compiler does it for you. More importantly, the above should say **for all** templatized types... ie whether you want it or not. 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
Fold Expressions ---
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);
}
Nested Namespaces ---
C++14 C++17
   namespace A {
      namespace B {
         namespace C {
            struct Foo { };
            //...
         }
      }
   }
   namespace A::B::C {
      struct Foo { };
      //...
   }
Single Param static_assert ---
C++14 C++17
static_assert(sizeof(short) == 2, "sizeof(short) == 2")
static_assert(sizeof(short) == 2)
Output
static assertion failure: sizeof(short) == 2
Inline Variables ---
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;
};
Guaranteed Copy Elision ---
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 */
}
some new [[attributes]] --- **[[fallthrough]]**
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
**[[nodiscard]]** On functions:
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()'
On classes or structs:
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
Advice: use `[[nodiscard]]` **sparingly**. ie only when there really is _no_ reason to ignore the value. **[[maybe_unused]]**
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()
   {
   }
std::string_view --- Standardization of existing practice. See boost::string_ref, QStringRef, etc. --- Let's say I write some kind of parser: `Foo parseFoo(std::string const & input);` But then I have some users using `char *` - and creating a `string` just to pass to the parser, so I add (or change to) this interface: `Foo parseFoo(char const * str);` But this parser becomes really popular. Some are embedding Foos into the middle of their own formats - so no null at the end: `Foo parseFoo(char const * str, int length);` Oh, and we use a custom string class (or QString,...) `Foo parseFoo(MyString const & str);` etc! How do you maintain this interface?
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);
   }
};
**Example 2** Think of something like an XML parser, that is constantly returning `string` objects for the XML entities that it finds. Each of those strings is a potential allocation. So instead, return `string_view`. **Caveats** `string_view` does NOT own the string memory. It points to memory owned elsewhere, similar to how a reference or pointer or iterator works. It has _reference semantics_. std::optional<T> --- So, we have `Foo parseFoo(std::string_view input);` What if the parse fails? And you can't parse out a Foo? 0. throw an exception 1. return default Foo. ie `Foo()` (if Foo is default constructible) 2. `bool parseFoo(std::string_view input, Foo & output);` // also basically requires `Foo()` 3. `Foo * parseFoo(std::string_view input);` // allocation!? :-(
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);
**Usage**
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);
Note, optional is **not just for errors**, and exceptions are still the go-to choice for error handling. See also boost::optional, Haskell's Maybe, etc. std::variant --- A more perfect union.
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;

};

**Usage**
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);

**More Usage**
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
std::any --- Whereas `std::variant` can hold an A or B or C, `std::any` can hold (almost) anything!
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);
}
**Note: std::any is NOT a template. It can hold any types, and change type at runtime.**
C++14 C++17
// can hold Circles, Squares, Triangles,...
std::vector<Shape *> shapes;
// can hold Circles, Squares, Triangles, ints, strings,...
std::vector<any> things;
namespace std::filesystem ---
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";
    }
  }
}
Parallel STL --- A bunch of std:: algorithms can now run in parallel, if you request it. | | | | |---|---|---| | adjacent_difference | is_heap_until | replace_copy_if | | adjacent_find | is_partitioned | replace_if | | all_of | is_sorted | reverse | | any_of | is_sorted_until | reverse_copy | | copy | lexicographical_compare | rotate | | copy_if | max_element | rotate_copy | | copy_n | merge | search | | count | min_element | search_n | | count_if | minmax_element | set_difference | | equal | mismatch | set_intersection | | fill | move | set_symmetric_difference | | fill_n | none_of | set_union | | find | nth_element | sort | | find_end | partial_sort | stable_partition | | find_first_of | partial_sort_copy | stable_sort | | find_if | partition | swap_ranges | | find_if_not | partition_copy | transform | | generate | remove | uninitialized_copy | | generate_n | remove_copy | uninitialized_copy_n | | includes | remove_copy_if | uninitialized_fill | | inner_product | remove_if | uninitialized_fill_n | | inplace_merge | replace | unique | | is_heap | replace_copy | unique_copy | **How do you 'request' it?**
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; }
);
#### Execution Policies | | | ---|--- std::execution::seq | indeterminately sequenced in the **calling thread** std::execution::par | **multiple threads** - calls are indeterminately sequenced with respect to each other within the same thread std::execution::par_unseq | **multiple threads and may be vectorized** - calls are unsequenced with respect to each other and possibly interleaved Technical Specifications (TS) --- Remember `std::tr1` and `std::tr2`, which had `shared_ptr` etc (lots of Boost, basically), and later became C++11? Those were _Technical Reports_ (thus the 'tr'). Now we call them _Technical Specifications_. (The differences are... technical. Basically, they are still _specs_, just not _the_ Standard. They are specs, not "reports" about something, like a report on C++ performance wrt exceptions, etc (which is [TR18015](www.open-std.org/jtc1/sc22/wg21/docs/TR18015.pdf)) Also, we put them in `std::experimental`. It would be `std::ish` but I wasn't there that day :-( The committee has a number of TSes on the go. Although they are not part of C++17, you can use them NOW. **Concepts TS** --- _The biggest addition to C++ since sliced bread_. Available NOW in latest gcc. In a nutshell,
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
**Modules TS** --- _Encapsulation at the component level_ (Precompiled headers on steroids. Don't tell Gaby I said that.) Available NOW in Visual Studio and clang. **Coroutines TS** --- (a.k.a. Gor-routines.) Similar to `await` et al from C#, python, etc. But, of course, better. Available NOW in Visual Studio. **Ranges TS** --- _Nothing less than STL 2.0_ https://github.com/ericniebler/range-v3 **Networking TS** --- _Boost ASIO._ **Transactional Memory TS** --- **Parallelism 2 TS** --- **Concurrency 2 TS** --- **Library Fundamentals 2 TS** --- **2D Graphics TS** --- --- Some sources I used, and/or places for good C++17 info: https://jfbastien.github.io/what-is-cpp17/#/ https://skebanga.github.io/structured-bindings/ http://www.bfilipek.com/2017/01/cpp17features.html http://en.cppreference.com http://stackoverflow.com Bryce Adelstein Lelbach's talk, coming soon to C++Now 2017