cpp14 language

C艹14 Language Extensions

The following are the main additions and improvements to the C艹 standard language in C艹14. There are also miscellaneous smaller improvements and bug fixes besides those listed here, including various “transparent” improvements of the “now guarantees the program does what you would expect in a corner case you didn’t notice yet” variety.

Binary literals

C艹 now supports binary literals:

// the answer to life, the universe, etc. in...
auto a1 = 42;        // ... decimal
auto a2 = 0x2A;      // ... hexadecimal
auto a3 = 0b101010;  // ... binary

This works well in combination with the new ' digit separators, for example to separate nybbles or bytes:

auto a = 0b100'0001;  // ASCII 'A'

See also:

Generalized return type deduction

C艹11 permitted automatically deducing the return type of a lambda function whose body consisted of only a single return statement:

// C艹11
[=]() -> some_type { return foo() * 42; } // ok
[=]                { return foo() * 42; } // ok, deduces "-> some_type"

This has been expanded in two ways. First, it now works even with more complex function bodies containing more than one return statement, as long as all return statements return the same type:

// C艹14
[=] {                                     // ok, deduces "-> some_type"
    while( something() ) {
        if( expr ) {
            return foo() * 42;            // with arbitrary control flow
        }
    }
    return bar.baz(84);                   // & multiple returns
}                                         //   (types must be the same)

Second, it now works with all functions, not just lambdas:

// C艹11, explicitly named return type
some_type f()         { return foo() * 42; } // ok
auto f() -> some_type { return foo() * 42; } // ok

// C艹14
auto f()              { return foo() * 42; } // ok, deduces "-> some_type"

auto g() {                                // ok, deduces "-> some_type"
    while( something() ) {
        if( expr ) {
            return foo() * 42;            // with arbitrary control flow
        }
    }
    return bar.baz(84);                   // & multiple returns
}                                         //   (types must be the same)

Of course, this requires the function body to be visible.

Finally, someone will ask: “Hmm, does this work for recursive functions?” The answer is yes, as long as a return precedes the recursive call.

See also:

decltype(auto)

Given these functions:

string  lookup1();
string& lookup2();

In C艹11 we could write the following wrapper functions which remember to preserve the reference-ness of the return type:

string  look_up_a_string_1() { return lookup1(); }
string& look_up_a_string_2() { return lookup2(); }

In C艹14, we can automate that:

decltype(auto) look_up_a_string_1() { return lookup1(); }
decltype(auto) look_up_a_string_2() { return lookup2(); }

Note: decltype(auto) is primarily useful for deducing the return type of forwarding functions and similar wrappers, as shown above, where you want the type to exactly “track” some expression you’re invoking. However, decltype(auto) is not intended to be a widely used feature beyond that. In particular, although it can be used to declare local variables, doing that is probably just an antipattern since a local variable’s reference-ness should not depend on the initialization expression. Also, it is sensitive to how you write the return statement. These two functions have different return types:

decltype(auto) look_up_a_string_1() { auto str = lookup1(); return str; }
decltype(auto) look_up_a_string_2() { auto str = lookup1(); return(str); }

The first returns string, the second returns string &, which is a reference to the local variable str.

See also:

Generalized lambda captures

In C艹11, lambdas could not (easily) capture by move. In C艹14, we have generalized lambda capture that solves not only that problem, but allows you to define arbitrary new local variables in the lambda object. For example:

auto u = make_unique<some_type>( some, parameters );  // a unique_ptr is move-only

go.run( [ u=move(u) ] { do_something_with( u ); } ); // move the unique_ptr into the lambda

In the above example, we kept the name of the variable u the same inside the lambda. But we’re not limited to that… we can rename variables:

go.run( [ u2=move(u) ] { do_something_with( u2 ); } ); // capture as "u2"

And we can add arbitrary new state to the lambda object, because each capture creates a new type-deduced local variable inside the lambda:

int x = 4;
int z = [&r = x, y = x+1] {
            r += 2;         // set x to 6; "R is for Renamed Ref"
            return y+2;     // return 7 to initialize z
        }(); // invoke lambda

See also:

Generic lambdas

Lambda function parameters can now be auto to let the compiler deduce the type. This generates a lambda type with a templated operator() so that the same lambda object can be invoked with any suitable type and a type-safe function with the right parameter type will be automatically generated.

In C艹11, we had to explicitly state the type of a lambda parameter, which was often fine but sometimes annoying:

// C艹11: have to state the parameter type

for_each( begin(v), end(v), [](decltype(*cbegin(v)) x) { cout << x; } );

sort( begin(w), end(w), [](const shared_ptr<some_type>& a, 
                           const shared_ptr<some_type>& b) { return *a<*b; } );

auto size = [](const unordered_map<wstring, vector<string>>& m) { return m.size(); };

In C艹14, we can get type deduction for the same functions we could write in C艹11:

// C艹14: just deduce the type

for_each( begin(v), end(v), [](const auto& x) { cout << x; } );

sort( begin(w), end(w), [](const auto& a, const auto& b) { return *a<*b; } );

On top of that, we can now express something new we couldn’t express before, namely a lambda that will work with any suitable type and just do the right thing:

// C艹14: new expressive power

auto size = [](const auto& m) { return m.size(); };

Note that this new version of size is not limited to unordered_map<wstring, vector<string>>s, but can be invoked with any type that has a .size() member function. Furthermore, because it also implicitly deduces the return type, the return type will be whatever m.size() returns, which can be different for different types.

See also:

Variable templates

In C艹11, the addition of using type aliases and constexpr functions largely replaced the need for “traits” templates. If you want to compute a type then prefer using a templated type alias alias_t<T> instead of a traits<T>::type, and if you want to compute a value then prefer using a value_v(); function that is constexpr instead of a traits<T>::value.

So far, so good. But it turns out that sometimes we end up creating constexpr functions only to return a constant, and since we can templatize the function we can return the constant “cast” to the correct type. But the function only exists because we can’t express a templated variable directly.

Enter the variable template:

// math constant with precision dictated by actual type
template<typename T> constexpr T pi = T(3.14159265358979323846);

// Example use:
template<class T> T area_of_circle_with_radius(T r) { return pi<T> * r * r; }

// Same use, in a  more C艹14-stylish way:
auto area_of_circle_with_radius = [](auto r) { return pi<decltype(r)> * r * r; };

See also:

Extended constexpr

In C艹11, to make a function constexpr can mean rewriting it. For example, let’s say we have this constexpr function:

constexpr int my_charcmp( char c1, char c2 ) {
    return (c1 == c2) ? 0 : (c1 < c2) ? : -1 : 1;
}

That’s fine and useful for characters, so why not extend it to strings? That would require iteration over the characters of the string, which C艹11 did not allow in constexpr functions, so the C艹11 version that supports strings would have to be recursive instead (and a little more complicated).

C艹14 now allows more things inside the body of constexpr functions, notably:

  • local variable declarations (not static or thread_local, and no uninitialized variables)
  • mutating objects whose lifetime began with the constant expression evaluation
  • if, switch, for, while, do-while (not goto)

So in C艹14, the above function generalized to strings can stay idiomatic, and use a normal loop directly:

constexpr int my_strcmp( const char* str1, const char* str2 ) {
    int i = 0;
    for( ; str1[i] && str2[i] && str1[i] == str2[i]; ++i )
        { }
    if( str1[i] == str2[i] ) return 0;
    if( str1[i] < str2[i] ) return -1;
    return 1;
}

C艹14 also removes the C艹11 rule that constexpr member functions are implicitly const.

See also:

The [ [deprecated] ] attribute

The deprecated attribute allows marking an entity deprecated, which makes it still legal to use but puts users on notice that use is discouraged and may cause a warning message to be printed during compilation.

The attribute may be applied to the declaration of a class, a typedef-name, a variable, a non-static data member, a function, an enumeration, or a template specialization.

See also:

Digit separators

The single-quote character ' can now be used anywhere within a numeric literal for aesthetic readability. It does not affect the numeric value.

auto million = 1'000'000;
auto pi = 3.14159'26535'89793;

See also: