There were three of us from Red Hat at the C++ meeting in Jacksonville, FL back in February, and it seems I never posted my trip report. So here it is now.

This was a fairly eventful meeting, as we're rapidly approaching C++17 and so the big question on everyone's minds was "What's going to make it in?" In the end, many things did, but not Concepts, which some had been hoping for as a headline item.

We started and finished the week looking at the list of Technical Specifications that we were considering incorporating into C++17.

First up was the Special Math Functions TS, which the math community has been pushing for since before C++11. Back in 2010 there was a lot of concern about the work and expertise necessary to get a decent implementation, but in the intervening years it has been a separate standard with multiple implementations, including libstdc++ and Boost, and so this time around there was no significant opposition.

The File System TS was up next; there have been multiple implementations in addition to the original Boost library, and issues resolved against the TS. Some people raised concerns about its suitability for unusual, e.g. non-hierarchical, filesystems, but there was general consensus that it should go in.

The Parallelism TS was also reported to have significant implementation experience, both with the TS specification and libraries that preceded it such as the libstdc++ parallel mode. There was some concern about possible future breaking changes, but not broad enough to delay inclusion. For more on the Parallelism and Concurrency view of the meeting, see Torvald's post.

Most of Library Fundamentals v1 (all but the invocation traits) was also adopted into the working paper.

Finally there was Concepts. There were a lot of people arguing both for and against integrating the TS into the working paper for C++17. Those arguing in favor pointed out that we've had an implementation for a while (in GCC snapshots since August 2015, usable on a development branch since about October 2014), and there's been significant user experience with that implementation, including development of the Ranges TS, and the user experience has been very positive. Furthermore, we're seeing more and more library proposals that rely on concepts, and we don't want to force them to rework to use enable_if hackery.

Those arguing against pointed out that there hasn't been a second implementation yet, there hadn't been a formally released compiler (though there is now), and there are still significant objections to certain aspects of the design (terse function template notations, function/variable duality, potential for future definition checking); these objections were raised during the TS process and deferred, but they still have not been resolved. This seemed over-cautious to me, but understandable after our experience with C++11 concepts.

In the end, the TS was not accepted into the working paper, so I've removed the feature from -std=c++1z for the GCC 6.1 release; users can enable concept support with the ‑fconcepts flag.

After the TS polling, we separated into subgroups. As usual, I spent most of the week in the Core working group (CWG), where we spent most of the week reviewing papers to introduce various smaller features that Evolution (EWG) approved for C++17, so we didn't do much processing of issues.

New features that completed Core review and were voted into the working paper at the end of the meeting:

The [[fallthrough]] attribute, equivalent to the [[clang::fallthrough]] attribute, or a lint fallthrough comment:

switch (i) {
  case 1:
    foo();
    [[fallthrough]]; // Don't warn about missing break.
  case 2:
    ...
}

The [[nodiscard]] attribute, similar to [[gnu::warn_unused_result]], but [[nodiscard]] can also be applied to classes, and can be suppressed by an explicit cast to void.

class [[nodiscard]] A { ... };
A f();

int main()
{
  f(); // warning: discarding nodiscard return value.
}

The [[maybe_unused]] attribute, equivalent to [[gnu::unused]].

[[maybe_unused]] static void
debug_dump() { ... } // don't warn if nothing calls this

P0138R2: Simplifying initialization of enum class variables from integers, so that users can write

E e {42};

rather than

E e = E{42};

P0017R1: Allowing aggregate initialization of base classes.

struct B { int i; };
struct D: B { int j; };
D d { {1}, 2 };

P0170R1: Making lambdas constexpr.

constexpr int AddEleven(int n) {
  return [n]{ return n+11; }(); // OK in C++17
}
static_assert(AddEleven(5)==16);

P0018R3: Lambda capture by of *this to capture the entire object by value.

struct A {
  int i;
  auto f() { return [*this]{return i;}; }
};
auto l = A().f(); // closure contains a copy of the A temporary
auto i = l();     // OK, no dangling pointer

P0245R1: C99 Hexadecimal floating point literals (which G++ has had since 3.0)

float f = 0xC.68p+2; // 49.625

Papers that got a lot of core review, weren't quite ready by the end of the meeting, but are expected to make it in at the Oulu meeting in June:

P0003R2: Removing deprecated C++98 (dynamic) exception-specifications. This has been coming for many years.

P0035R2: 'new' of C++11 over-aligned types, so that

class alignas(64) cache_aligned_int { int i; };
auto p = new cache_aligned_int;

will actually have the correct alignment, rather than just what malloc happens to provide.

P0292R0: if constexpr to avoid instantiating sections of a function template controlled by a false constant-expression, e.g.

template<typename T, typename ... Rest> void g(T&& p, Rest&& ...rs) {
  // ... handle p
  if constexpr (sizeof...(rs) > 0)
    g(rs...);  // never instantiated with an empty argument list.
}

Without the if constexpr, we would need another overload of g to handle the case where rs is empty.

P0252R0: operator. for smart references in the same way that smart pointers define operator->.

struct A { int i; } a;
struct B { int j; } b;
struct C {
  A& operator.() { return a; };
  B& operator.() { return b; };
} c;
void f() { c.i = c.j = 42; }

P0145R1: Setting the evaluation order of many operations that are currently unspecified, to fix chaining of member function calls. In this example from the paper,

void f()
{
  std::string s = "but I have heard it works even if you don't believe in it"
  s.replace(0, 4, "").replace(s.find("even"), 4, "only")
   .replace(s.find(" don't"), 6, "");
  assert(s == "I have heard it works only if you believe in it");
}

The currently unspecified order of evaluation means that the find calls could be evaluated before the first replace call, which then makes their results wrong.

The current sentiment of the committee is to also specify that normal function
arguments are evaluated from left to right. This will be a significant change
for GCC, which currently evaluates arguments from right to left on many
targets, including x86_64; now that GCC 6 is out, I want to try forcing
left-to-right evaluation in G++ to see what impact it has on our benchmarks.

P0251R0:
Unified function call syntax, calling member functions and free functions using
the same syntax.

  struct A { void f(); } a;
  int main() { f(a); } // treated as a.f();

This was expected to be accepted at the meeting, but ended up being withdrawn
for revision; it seems likely that the call syntax will change.

Also Torvald's forward progress paper, discussed in his blog post.

Papers that haven't gotten significant core review yet, but EWG would like to see in C++17:

P0221R1: Compiler-generated comparison operators, providing implicit definitions of operator== and such as appropriate. This proposal also tries to prevent common slicing bugs by prohibiting copy construction or assignment from a derived class without some obfuscation:

struct B { int i; };
struct D: B { int j; };

int main()
{
  D d { {1}, 2 }; // C++17 aggregate initialization
  B b (d);        // OK in C++14, now ill-formed
  B b (static_cast<B&>(d)); // OK
}

If this passes, I expect it to be a source of porting headaches for people moving to C++17, but I'm sure implementations will provide a way to accept affected code with a warning.

P0091R1: Omitting template arguments for class templates when they can be deduced from an initializer, to avoid the need for function templates like make_pair:

std::pair p (2, 4.5); // pair<int,double>

N4424: Inline variables, allowing users to define variables with linkage like a static
data member of a template without actually needing to wrap it in a template.

struct WithStaticDataMember {
  // This is a definition, no out­-of-­line definition is required.
  static inline constexpr const char *kFoo = "foo bar";
};

The next meeting is in Oulu, Finland in the middle of June; this will be an important meeting, as it will determine the final feature set for C++17.

Last updated: February 6, 2024