Trip report: Summer ISO C++ standards meeting (Oulu)

On June 25, the ISO C++ committee completed its summer meeting in Oulu, Finland, hosted by Symbio and the Finnish national body.

We again had some 100 experts officially representing nine national bodies. As usual, we met for six days Monday through Saturday, and around the clock from 8:30am till 11pm most days – evening sessions regularly went a little later than the usual 10pm because it was Midsummer and there was literally no full night all week long at that short distance from the Arctic Circle, and people commonly did a double take when they looked at their watches and were surprised to find that it was already nearly midnight.

Here’s a summary of what happened, with some details about the current ISO C++ process so you can see just how the work is progressing and getting released. I’ve tried to add some links to the relevant feature design papers, or to the papers that summarize what was done which in turn usually carry more links to the design papers.

C++17 is feature-complete, enters review period

The big news is that C++ is feature-complete, and on time! We added several more features to the C++17 working paper (see next section), then approved the result to be sent out this summer for its major ISO international comment ballot, which is the Committee Draft or “CD” ballot.

Between now and our November meeting, national bodies around the world will be reviewing the draft and reporting any concerns about feature design details and wording correctness or consistency. Then for probably the next two meetings we’ll be addressing every national body comment and recording its resolution, as well as continuing to process our own known issues lists to fine-tune the text of the standard. That usually takes two meetings, and if that’s the case again this time then we’ll be putting the finishing touches on C++17 in our November and March meetings and then, we hope, sending C++17 out for its possibly-final ballot in the spring. If we need an extra meeting, then that would extend to the July meeting next year and the possibly-final ballot in late summer.

So C++17 is and tracking to ship on schedule next year. In my March trip report, I mentioned that after completing C++11 we switched to a “train model” where we have been shipping the standard consistently every three years. So far, we have been running the trains on time: C++14 was actually the first C++ standard ever that shipped when expected, and now C++17 is on track to do it again. This is excellent news for the community because it enables implementers to track the standard closely, whereas in previous releases they often held back from implementing new features aggressively because with an open-ended schedule the committee could (and did) change its mind again about the design of some feature before the standard actually shipped. The ISO C++ committee is now sticking to a high quality bar, voting features in when they’re stable and releasing on time, which removes uncertainty and is a major reason why compilers are more in sync than ever before: After C++98 shipped, it took 5 years before we saw the first complete conforming compiler that implemented all language features (modulo bugs of course); after C++11 shipped, it took 3 years; when C++14 shipped, it took months. Now, many C++17 features are already available in major compilers, and I wouldn’t be surprised if C++17 repeated C++14’s synchronization with the community so that we see at least one major implementation that has all C++17 features in the same year that C++17 is published. This is great news for the community, because it means we have to play less of the “which compilers actually implement which features” game; true, we still need to deal with older compilers, and some compilers are still not as quick as others to ship the latest features, but all of the major compilers’ current releases are in closer sync with the standard than they’ve ever been before and even the delta between them continues to shrink, which is good news for all C++ users.

Note: Last time I mentioned that there was some thought of moving to a two-year cadence after C++17, but for now we’re staying with three years, so the next standard after C++17 will be C++20.

More features added to C++17

At this meeting, we added several more features into C++17; you can find a good summary of those features in Bryce Lelbach’s Reddit post. Again, note that these are just the ones we added at this meeting; the complete list of C++17 features is longer. For example, see my March trip report for what was added at the spring meeting.

Language features

Here are some of the more noticeable language features we added to C++17 in Oulu. I’ll merge from Bryce’s nice list (thanks Bryce!), my own comments in the previous trip report, and some new comments and examples:

Dynamic memory allocation for over-aligned data: Extending the C++11 alignof/alignas for heap allocated memory control.

Template argument deduction for constructors, so that you can write just pair p(2, 4.5); instead of pair<int,double> p(2, 4.5); or auto p = make_pair(2, 4.5);. This is pretty sweet, including that it obsoletes many “make” helpers.

template <auto>: Recall that templates can take “non-type parameters,” or ordinary compile-time values; a familiar example is std::array<widget,10> which is an array of 10 widget objects, passing 10 as a compile-time number. Well, as of C++11 we allow “auto”matically deducing the type of local variables:

auto i = 10;           // deduces int

and in the Concepts TS we allow “auto”matically deducing the type of parameters:

void f(auto value) { } // same as template <class T> void f(T value);

f(10);                 // deduces int

Now, with the template<auto> extension, C++17 will additionally allow the same to happen when you pass a value as a template parameter, which is just another place you can pass a value:

template <auto value> void f() { }

f<10>();               // deduces int

Guaranteed copy elision: When you call a function that returns an object by value, and you use the copy to initialize a local variable, the language has always said that you copy (or move) twice. You may know that, since forever, C++ has also added, “but by the way, the compiler is allowed to elide (disappear) the extra copy.” With guaranteed copy elision, in many cases the C++17 language now says you copy (or move) once; that is, compiler is now required not to perform an extra copy or move, so that copy elision is no longer an optimization but a guaranteed language feature.

Richard Smith, the proposal author, provided the following additional notes and examples that show how the copy elision guarantee depends on what happens inside the function. In some cases, such as when the function returns a temporary of the right type, the new rules guarantee that zero copies will be performed, and the return type is not even required to be copyable or movable any more:

T f() {
  return T{}; // no copy here (C++17)
}

T x = f();    // no copy here either (C++17)

In other cases, where the named return value optimization (NRVO) would apply today, there is one copy and the compiler may elide the copy, but is not guaranteed to do so because there are cases where that could not be guaranteed:

T g() {
  T t;
  return t;   // one copy, can be elided but elision is not guaranteed
}

T y = g();    // no copy here (C++17)

And if the function returns something that really requires a copy, such as returning a reference parameter, you get exactly one copy:

T h(T &t) {
  return t;   // one guaranteed copy (by necessity)
}

T z = h(x);   // no copy here (C++17)

Order of expression evaluation guarantees: This removes portability and usability bugs. In a nutshell, a number of code examples that you thought worked, now actually do work. In particular, this solves the long-standing f(new T, new T) bug that I wrote about nearly 20 years ago, fixes some examples in published books, and makes the brand-new future<T>::then chaining actually work correctly.

Inline variablesThis feature makes it easier to define global variables (that’s the bad news) correctly (that’s the good news), including in header files. The net effect is that some code people already write in practice, but shouldn’t have because it had subtle pitfalls, now works.

if constexpr: This is a very powerful feature that allows branches that are evaluated at compile time. Note that this is a disciplined compile-time “if”, not just text substitution; the code in a false branch needs to be syntactically well-formed, but doesn’t need to be semantically valid.

This lets you express some pretty powerful stuff, including that a function template can write all the special cases of its algorithm right within its body (e.g., a fast algorithm for random-access iterators, and a fallback for less powerful iterators), without writing a set of template specializations.

It also works very nicely with C++14’s “auto” return type deduction for inline functions: It’s perfectly fine for two mutually exclusive if constexpr branches to return unrelated types since only one of the branches can be taken for a given set of inputs, and the correct return type will be deduced. Of course, within the same if constexpr branch the return types have to be compatible according to the current rules. In fact, we’ll see an example of this in the next topic…

Structured bindings: This allows taking a value that contains multiple elements, such as a tuple or a struct, and binding convenient names to the individual elements – much like std::tie, except without having to have variables of the correct type already available. I was pleasantly surprised to see this one make it into C++17 even though it came in late in the cycle. Here’s a simple example of what it enables:

tuple<T1,T2,T3> f();
auto [x,y,z] = f(); // types are: T1, T2, T3

map<int,string> mymap;
auto [iter, success] = mymap.insert(value); // types are: iterator, bool

struct mystruct { int i; string s; double d; };
mystruct s = { 1, “xyzzy”s, 3.14 };
auto [x,y,z] = s; // types are: int, string, double

What if you want to add structured bindings for your own type that isn’t a struct or a tuple? I’m glad you asked, because it’s an excuse to also show off also how you can use if constexpr. Let’s say you’re the author of a type S:

class S {
   int i;
   char c[27];
   double d;
public:
   // ...
};

Now you want to allow structured bindings for your type S, and for extra marks you don’t want to bind directly to the private variables but perhaps want member c to be bound as a string_view (also adopted for C++17; see below). Here’s how do add this ability; it’s a few lines, but you only have to do it once for all users of type S:

// 1. Add tuple_size and tuple_element support
namespace std {
   template<> struct tuple_element<0,S> { using type = int; };
   template<> struct tuple_element<1,S> { using type = string_view; };
   template<> struct tuple_element<2,S> { using type = double; };
   template<> struct tuple_size<S>: public integral_constant<size_t,3> {};
}

// 2. Now add get<> support (using C++17, because why not; it’s better
// than =delete’ing the primary get<> template and adding specializations)
template<int I>
auto get(const S&) {
   if      constexpr(I == 0) return x.i;
   else if constexpr(I == 1) return string_view{x.c}; }
   else if constexpr(I == 2) return x.d;
}

Now all users of S can write code like this:

S f();  // some function that returns an S

auto [ n, s, val ] = f(); // types are int, std::string_view, and double

if (init; condition) and switch (init; condition): Just like the venerable for (int i = 0; /*…*/) loop has always allowed declaring a variable (in that example, i) that exists for the scope of the loop, we will now be able to do the same for if and switch without having to resort to today’s workaround of declaring the variable before the if or switch and then wrapping it up in { } to restrict its scope. Here’s an example of the new cleaner syntax, which allows more tightly-scoped variables:

map<int,string> mymap;

if (auto result = mymap.insert(value); result.second) {
    // insert succeeded, and result is valid for this block
    use(result.first);  // ok
    // ...
} // result is destroyed here

In fact, you might have noticed that I used a similar example for both structured bindings and for if/switch initialization. It took Reddit about 30 minutes to discover that they’re great together, like peanut butter and jelly:

// better together: structured bindings + if initializer

if (auto [iter, succeeded] = mymap.insert(value); succeeded) {
    use(iter);  // ok
    // ...
} // iter and succeeded are destroyed here

Two language features that didn’t make it (yet)

Of the new language features I mentioned last time, only two didn’t make it into C++17:

  • operator. (dot) to allow smart references (in parallel with smart pointers) and much more. During final core language wording review, the authors discovered a problem that couldn’t be fixed in real time at this meeting, and was pulled back while the authors fix the proposal. This proposal is still active and is expected to come back with a fix to that problem very soon, possibly as soon as in the next week or two for the post-meeting mailing; I’m hearing some rumblings that national bodies might mention this in their summer ballot comments, so we might see it again during C++17 ballot resolution, else I’d expect it to be one of the first things added to the draft standard once C++17 ships.
  • Defaulted comparisons, to generate ==, !=, <, <=, >, >= for types that don’t write them by hand. The current version of the proposal is opt-out, and would generate comparisons for existing types. When the proposal got to the full committee, we discovered that the committee didn’t have consensus to approve the paper’s opt-out model; a number of people spoke up for an opt-in model instead. This proposal might eventually come back but probably not soon.

Standard library features

Here are some of the library features that made it into C++17 at this meeting:

variant<>: Don’t use raw unions any more. This addition completes the trifecta of std::any and std::optional (which were already added to C++17). The new std::variant is a type-safe union; for more background see also my fall meeting trip report. Here’s an example adapted from the paper that shows how you can use it:

variant<int, float, string> v, w;
v = “xyzzy”;         // now v contains a string
v = 12;              // now v contains an int

int i = get<int>(v); // ok, because it contains an int

w = get<int>(v);     // ok, assign to another variant
w = get<0>(v);       // same effect as the previous line
w = v;               // same effect as the previous line

get<double>(v);      // compile-time error: v can’t contain a double
get<3>(v);           // compile-time error: v doesn’t have 4 types

try {
  get<float>(w);     // will throw: w contains an int, not a float
}
catch (bad_variant_access&) {}

Moving nodes between map<>, unordered_map<>, set<>, and unordered_set<>: You will now be able to directly move internal nodes from one node-based container directly into another container of the same type (differing at most in the comparator template parameter), either one node at a time or the whole container. Why is that important? Because it guarantees no memory allocation overhead, no copying of keys or values, and even no exceptions if the container’s comparison function doesn’t throw. The mechanics are provided by new functions .extract and .move, and corresponding new .insert overloads. Here’s an example adapted from the paper:

map<int, string> src {{1,”one”}, {2,”two”}, {3,”buckle my shoe”}};
map<int, string> dst {{3,”three”}};

dst.insert(src.extract(src.find(1))); // iterator version
dst.insert(src.extract(2));           // key type version
auto r = dst.insert(src.extract(3));  // key type version
    // note: this last one will fail because dst already contains 3;
    // the node is owned by r, the returned struct

// At this point we have the following state:
//   src == {}
//   dst == {{1,“one”}, {2,“two”}, {3,“three”}}
//   r.position == dst.begin() + 2
//   r.inserted == false
//   r.node == {3,“buckle my shoe”}

// We can go back and retry the failed insert with a different key:
r.node.key() = 4;                      // use a non-conflicting value
dst.insert(r.position, std::move(r.node));  // now succeeds

Extended memory management tools for in-place construction and destruction: These include destroy(_at|_n), uninitialized_move(_n), uninitialized_value_construct(_n), and uninitialized_default_construct(_n).

In case you missed it: string_view also in C++17

C++17 also contains a number of other useful features. I’ve mentioned these before, including “small” but widely-used features like optional and any, right up to Parallel STL. But let me just take a moment to remark again on the wonderfulness of string_view, which is a non-owning view of a contiguous string owned by someone else.

If you have a const string& parameter, consider just changing it to string_view. As Mark Isaacson mentioned in his NDC talk earlier this month, it’s pretty rare that you can get a performance boost by just doing a global search-and-replace, but Mark points out you can pretty much do that by globally replacing const string& with string_view (with one exception noted in the next paragraph). Why is this often more efficient? In particular, when the caller passes something like a string literal as an argument, if your function takes it via a const string& parameter that means the caller has to convert the string literal to a temporary string object, which typically incurs a heap allocation and deallocation, whereas passing it to a string_view incurs no allocation overhead at all.

However, beware one potential pitfall, especially until more of the world has migrated to string_view: If your function f(/*some string*/ x) internally calls one or more other functions that still take a string& (either const or non-const), then let your function f continue to take x by const string& and don’t change it to string_view. The reason is that if f is called with an actual string object, then f(const string& x) can pass it along painlessly by reference, whereas f(string_view x) would avoid a copy when entering f but would need to create a new string internally just to pass it along to the other function that needs a string. Bottom line: If you know your function needs to call other functions that you don’t control and that take (possibly const) string&, then you should probably leave your function taking const string& too.

C++17: Recognizably new

C++17 will pervasively change the way we write C++ code, just as C++11 did. As these and other new features become available, we’re going to see new code using structured bindings, if/switch scope variables, string_view, optional, any, variant, Parallel STL, and more all over the place.

Here’s my personal litmus test for whether we’ve changed the way people program: Just as you can take a look at a screenful of code and tell that it’s C++11 (not C++98), if you can look at a screenful of code and tell that it’s C++17, then we’ve changed the way we program C++. I think C++17 will meet that bar.

Here’s a little “C++17 demo” put together by committee member Thomas Köppe, who is the author of the if/switch initializer proposal. It shows off a few of the features that will soon be available as part of C++17, and how they work together. Note this uses some other new C++17 features I didn’t specifically mention. I’ll let the comments speak for themselves; thanks, Thomas, for providing this demo.

unordered_map<string, unique_ptr<Foo>> items;
vector<unique_ptr<Foo>> standby;

// Desired semantics of f: If there is currently no item 'id', install
// 'foo' as item 'id'. Otherwise, put 'foo' on the standby list for later.

//------------------------------------------------
// Before C++17: 7 lines + 4 pitfalls
//
void f(string id, unique_ptr<Foo> foo) {
   auto it = items.find(id);
   if (it == items.end()) {
      auto p = items.emplace(move(id), move(foo));
      p.first->second->launch();
   } else {
      standby.push_back(move(foo));
      standby.back()->wait_for_notification();
   }

   // Notes:  
   // * Variable 'id' can no longer be used (moved-from); or...  
   // * ...would need to pass by 'const string& id' and force copying.
   // * Map lookup performed twice. Ordered map could use lower_bound +
   //   hint, but unordered map cannot.  
   // * (Cannot emplace unconditionally, because it might destroy *foo.)
}

//------------------------------------------------
// With C++17: 4 lines + none of those pitfalls
//
void f(string_view id, unique_ptr<Foo> foo) {
   if (auto [pos, inserted] = items.try_emplace(id, move(foo)); inserted){
      pos->second->launch();
   } else {
      standby.emplace_back(move(foo))->wait_for_notification();
   }
}

Other progress

Because this time our focus was on completing C++17, we didn’t spend as much time on the other TSes as we did at a normal meeting, but we made progress on some of the TSes and will continue with all of them next time. In particular, the Reflection and 2D Graphics proposals received several hours of review each and are making great progress; at our next meeting we’ll continue with Ranges, Networking, Library Fundamentals 2, Parallelism 2, Modules, Coroutines, Contracts, and more.

What’s next

This summer, we’re going to send out the feature-complete C++17 out for its major international comment ballot. After that, we’ll address ballot comments at the November 2016 and February-March 2017 meetings and plan to send C++17 out for its final approval ballot then.

Once C++17 ships next year, I expect we’ll start looking at merging more of the TS “beta branches” into the C++ “trunk.” Here’s an updated snapshot of our status:

wg21-timeline

Thank you again to our host Symbio, to the over 100 experts at Oulu last week, and to the many more who participate in standardization through their national bodies, without whose efforts and willing collaborative spirit these results would not be achievable. The over four million C++ users worldwide benefit, and appreciate it very much too. Thank you, all, for your many contributions to Standard C++.

54 thoughts on “Trip report: Summer ISO C++ standards meeting (Oulu)

  1. Pingback: Site Title
  2. Good news on C++17, grats!

    When you get a break, and Andrei can tear himself away from D for a bit… any chance of revising for a new C++17 Coding Standards? :-)

  3. Herb, are there any news re: ‘relaxed’ semantics? Is it expected to be changed somehow?
    Also, have ARM and PowerPC compilers adopted it better? Or do they still emit ‘relaxed’ operations as memory barriers?

  4. Thanks for the summary.

    A minor issue, but on Chrome some of the code samples are hard to read due to the font selection – simply too small.

  5. > literal -> const string&

    I love string_view!
    Why aren’t compilers able to construct the std::string at compile time (into read-only memory)?
    When can we expect string_view in MSVC?
    What happened to string_span etc from C++ core guidelines?

  6. Yes, this code would fail to compile:

    variant<int,double,char> f() { /*...*/ }
    
    auto [x,y,z] = f(); // error, because a variant is not a structure with members that can be bound
    
  7. thank you . so ‘tuple_size’ was what I missed. the code snippet i n my last comment should fail to compile, right?

  8. @F.Mehrabi: No, because a variant is not a structure of N values to which structured binding would apply, it’s a sum type that holds a single value (1 of N alternatives but only 1 at a time). That’s why variant deliberately doesn’t advertise itself as a tuple, which would be incorrect; in particular it doesn’t specialize tuple_size, which is what is required by structured bindings (for everything but arrays which are special-cased). See the variant proposal paper linked… quoting:

    Rename tuple_size to variant_size, tuple_element to variant_alternative to clarify that this is not tuple-like. This avoids a clash with structured binding.

  9. Isn’t there a conflict between structured-binding and variant? variant interfaces an int argument-ed get with mutually exclusive integer inputs.So is there a possiblity of run-time exeption in the following example?

    
    auto f(){
        std::variant<int,double,char> var='x';
        ...
        return var;
    };
    
    auto [x,y,z] =f();//can this compile and run without exception???
    
    

    I smell trouble unless I am missing something(e.g get(variant) returns optional or it is renamed to something else…).

  10. Now that C++17 is feature complete, is there a definitive write up that covers the 17/14 delta?

  11. @GregM: I don’t know of a writeup of the reasons, but even though no variation of defaulted comparisons has got consensus yet, people are still noodling over alternatives that might.

    @Mark: I like LEWG would welcome such a proposal. Check out https://isocpp.org/std/submit-a-proposal for details.

    @Marcin: Yes, they’d need to have access.

    @Fabio: Yes, the order of evaluation guarantees fix the f(new T, new T) bug that make_unique was a workaround for today. See the link in that section.

    @admin: Yes, that should be {x.c}. Fixed, thanks.

    @Sebastian: Nested bindings were deliberately excluded from the initial proposal but might be added later.

    @Andy: We won’t need the make_* helper for the purpose of type deduction, but in cases likemake_shared where they do more than just deduce the helper will still be needed for the additional non-deduction functionality.

  12. @Giuseppe: Totally agreed. This is where people like Scott Meyers used to inject some sanity into the mix by researching all the features and culling them into a single book. Sadly Scott has bid adieu to C++…

  13. Really helpful as usual Herb, the only thing that worries me is that at this level of innovation it is becoming very difficult to keep track of everything… someone is still struggling to introduce modern c++11 in production.

    We need more books and publications, We, average Joe programmers, need some guidance I think the effort with GSL is awesome i think!

  14. @Fabio: The problem with a forwarding ctor on

    unique_ptr

    is that it’d either be dangerous in the case of a single pointer argument, or inconsistent if it only forwarded multiple arguments. Imagine if MyClass had a constructor like

    MyClass(MyClass* parent);

    , where the parent is assumed to outlive the constructed instance. Then

    unique_ptr<MyClass>(parent)

    would lead to a double-delete instead of constructing a new instance.

    You could fix that by requiring a tag as the first argument, kinda like

    std::pair

    does with

    std::piecewise_construct

    :

    unique_ptr<MyClass>(std::construct_new, parent);
    

    But at that point it doesn’t seem like much of a win compared to

    make_unique

    .

  15. Wow, I guess there was a bit of a moderation delay getting those replies posted.
    Six answers written before any of them appeared.

    Andy, I don’t imagine it will be possible to eliminate make_shared. The constructor can’t possibly do it because the allocated memory block needs to be larger than shared_ptr itself. By the time the constructor is called, it’s too late to affect that.

  16. I’m curious, with template argument deduction for constructors obviating the need for most

    make_*()

    functions, if

    make_shared()

    will still be required if we want the reference counting control block to be allocated alongside the actual object data, or if that optimization can also now be handled via the

    share_ptr

    constructor directly.

  17. @Duarte/Furkan try_emplace is guaranteed to not touch its arguments if the key already exists in the container. That’s approximately the whole point of it.

  18. The other half of that answer is the entire reason for try_emplace, the guarantee that it won’t move from the object if it already exists and thus isn’t added.

  19. move(foo) does not move from foo. It simply says that foo is eligible to be moved from.
    A less confusing name would have been rvalue_cast(), and I think that was discussed at one point but it was too late in the process to make that change.

  20. To the couple of people asking about the twin calls to std;:move, std::move itself does nothing except cast its argument to an rvalue reference, so you can call it on an object as many times as you want. It’s the thing that receives an rvalue reference that can muck about with the innards of the object, and try_emplace promises not to do that if the key is already there.

  21. @Duarte: In the C++17 example foo is not moved from twice. It is only converted to an rvalue reference twice. Function try_emplace now may or may not move from the object, depending on whether it would be a duplicate or not.

  22. @sv90: the for-each loop works with structured bindings. Your example is correct under P0217R3:

    map<int, string> mymap;
    //...
    for (auto [key, value] : mymap) {
     //...
    }
    
  23. @Duarte [std::move(foo)] simply casts to an rvalue reference but it does not actually perform the move itself. If [try_emplace] returns false, then ownership of [foo] has not been claimed, but [foo] just hangs around as an rvalue reference. In that case, the else branch will perform a second [std::move(foo)], which just performs another rvalue-reference cast. An rvalue-reference cast on top of an rvalue reference results in just an rvalue reference. So nothing dangerous happening.

  24. Great summary! Just one question: in the C++17 version of the demo, how is it okay to move twice from foo, in case the else branch is taken?

  25. Addendum to my previous comment.

    I do recognize there’d be no way for a std::*_ptr to both deduce its template argument and implement a forward constructor, because there’d be no way to know which class’ constructor the arguments oughta be forwarded to.

    However, It’d be nice to be able to do something like this:

    std::unique_ptr<MyClass> myObject(arg1, arg2, arg3);
    

    instead of doing

    auto myObject = make_unique<MyClass>(arg1, arg2, arg3);
    
  26. void f(string_view id, unique_ptr<Foo> foo) {
       if (auto [pos, inserted] = items.try_emplace(id, move(foo)); inserted){
          pos->second->launch();
       } else {
          standby.emplace_back(move(foo))->wait_for_notification();
       }
    }
    

    Would it be possible to nest structured bindings like so?:

    void f(string_view id, unique_ptr<Foo> foo) {
       if (auto [*[new_id, new_foo], inserted] = items.try_emplace(id, move(foo)); inserted){
          new_foo->launch();
       } else {
          standby.emplace_back(move(foo))->wait_for_notification();
       }
    }
    

    I really want to get rid of those error-prone and confusing “->first” and “->second” statements in my code alltogether.

  27. One question about structured bindings:
    Will this be possible in C++17?

    map<int, string> mymap;
    //...
    for (auto [key, value] : mymap) {
     //...
    }
    

    If not, is there a special reason why this is not desirable?

  28. This looks incorrect in your structured binding example:

    else if constexpr(I == 1) return string_view{c}; }
    

    It should be:

    else if constexpr(I == 1) return string_view{x.c};
    
  29. First off, great job!

    I’m especially gonna love the “template argument deduction for constructors”, what about the std::*_ptr classes though? In one of your past posts you showed an implementation of make_unique whose usefulness goes beyond just saving keystrokes, having more to do with exception safety.

    Correct me if I’m wrong, to be consistent with the new change, std::unique_ptr and std::shared_ptr would have to have a forwarding constructor: will that be the case?

  30. In C++17 version

    wouldn’t items.try_emplace(id, move(foo)
    move foo even if it doesn’t emplace,
    and then using it in the else block result in undefined behaviour

  31. The example with if-constexpr’ed get, shouldn’t this get() be BFF with S? The rule of accessing members (here, private members) didn’t change, did it?

  32. Thanks for the timely update! The reference to my talk is also much appreciated :). Hoping we’ll cross paths at CppCon.

    One specific unfortunate example where the const string& -> string_view conversion breaks down that a colleague discovered recently is in functions that need to search an unordered_map. As of C++14 map has the ability to compare with types other than key_type, but unordered_map’s find function won’t accept anything other than key_type. I’d expect std::hash to be the same as std::hash… so… hoping there’s something to be done in this space. Willing to try and put forward a proposal if it sounds like a worthy cause to you/if I’m not missing an obvious blocker.

  33. “When the proposal got to the full committee, we discovered that the committee didn’t have consensus to approve the paper’s opt-out model; a number of people spoke up for an opt-in model instead. This proposal might eventually come back but probably not soon.”

    That’s too bad. Is there a summary of the reasons that were given for not approving the paper’s opt-out model?

Comments are closed.