Tag Archives: C++17

C++17: Lambdas

C++ has supported lambda functions since C++11, and post-C++14 even supported generic lambdas. C++17 adds a quirky feature to enable ‘copy capture’ of ‘this’. Here’s an example of it in action:

struct MyStruct
{
    auto lambda_with_this() // Before C++17, copies this pointer
    {
        auto f = [this]{ return value; };
        return f;
    }
    
    auto lambda_with_star_this() // C++17 - takes a local copy of *this
    {
        auto f = [*this]{ return value; };
        return f;
    }
    
    int value;
};

TEST( Cpp17, lambda_capture_this )
{
    MyStruct s{ 42 };
    auto f = s.lambda_with_this();
    s.value = 101;
    EXPECT_EQ( 101, f() );
}

TEST( Cpp17, lambda_capture_star_this )
{
    MyStruct s{ 42 };
    auto f = s.lambda_with_star_this();
    s.value = 101;
    EXPECT_EQ( 42, f() );
}

Notice that in the second case, we capture a copy of our object – so the lambda returns the value held at the point of capture (42) rather than the value when it is called (101). This can be very important if ‘this’ has been destroyed between the creation of the lambda and the time at which it’s called.

Now, C++14 also supported generalised lambda capture, which meant you could (re-)name variables when capturing (and provided a neat way to capture-by-move):

    auto f = [tmp = *this]{ return tmp.value; };

But the C++17 code is more concise. See this useful StackOverflow post for more discussion.

Another advance in C++17 is that lambdas are implicitly constexpr – so you can now use them in compile-time contexts, like declaration of std::array:

// lambda explicitly constexpr since C++17
auto square = []( auto v ){ return v*v; }; 

TEST( Cpp17, lambda_implicitly_constexpr )
{
    // std::array calls 'square()' at compile time
    std::array<int, square(4)> values; 
    EXPECT_EQ( 16, values.size() );
}

See also the previous C++17 post.

Leave a comment

Filed under C++, C++ Code, Programming

C++17: Nested Namespaces

Another crowd-pleaser in C++17 is the ability to declared nested namespaces without literally nesting them. In the past, you had to do this, which involves a lot of wasted whitespace:

namespace NestedNamespaces
{
    namespace Really
    {
        namespace Work
        {
            auto return_int(){ return 42; };
        }
    }
}

Happily, you can now do this instead:

namespace NestedNamespaces::Really::Work
{
    auto return_int(){ return 42; };
}

TEST( Cpp17, nested_namespaces )
{
    EXPECT_EQ( 42, NestedNamespaces::Really::Work::return_int() );
}

See also previous C++17 post.

1 Comment

Filed under C++, C++ Code, Programming

C++17: Structured Bindings

This is one of a series of posts on C++17 features – see also previous post on if initialisers.

Structured bindings are a convenient way of handling multiple return values from functions. Whilst F# has been able to do this:

let f() = 42, "Hello, World" // return a pair of values
let a, b = f() // assign a and b to the values returned by f

in C++, we’ve had to declare the variables up front and use std::tie to assign values (so not only does this take more lines, we also have to default initialise the variables then throw away the defaults).

auto t = std::make_tuple( 42, "Hello, World" );
int a, b;
std::tie( a, b ) = t;

The new structured bindings are much more concise, even if the use of square brackets came as a surprise. Even better, you can use structured bindings with structs and std::array.

int my_int{ 42 };
std::string my_string{ "Hello, World" };
bool my_bool{ true };

auto return_pair()
{
    return std::make_pair( my_int, my_string );
}

auto return_tuple()
{
    return std::make_tuple( my_int, my_string, my_bool );
}

struct MyStruct
{
    int a;
    double b;
    int c;
    
    static MyStruct Expected;
};

MyStruct MyStruct::Expected = { 1, 2.2, 3 };

auto return_struct()
{
    return MyStruct::Expected;
}

auto return_array()
{
    return std::array<int,3>{ 1, 2, 3 };
}

auto return_map()
{
    return std::map<int, std::string>{ {1, "a"}, {2, "b"}, {3, "c"} };
}

TEST( Cpp17, structured_bindings_for_pair )
{
    auto [i, s] = return_pair();
    
    EXPECT_EQ( my_int, i );
    EXPECT_EQ( my_string, s );
}

TEST( Cpp17, structured_bindings_for_tuple )
{
    auto [i, s, b] = return_tuple();
    
    EXPECT_EQ( my_int, i );
    EXPECT_EQ( my_string, s );
    EXPECT_EQ( my_bool, b );
}

TEST( Cpp17, structured_bindings_for_struct )
{
    auto [i1, d, i2] = return_struct();
    
    EXPECT_EQ( MyStruct::Expected.a, i1 );
    EXPECT_EQ( MyStruct::Expected.b, d );
    EXPECT_EQ( MyStruct::Expected.c, i2 );
}

TEST( Cpp17, structured_bindings_for_array )
{
    auto [i1, i2, i3] = return_array();
    
    EXPECT_EQ( 1, i1 );
    EXPECT_EQ( 2, i2 );
    EXPECT_EQ( 3, i3 );
}

TEST( Cpp17, structured_bindings_for_iterating_over_map )
{
    for ( const auto& [key,value] : return_map() )
    {
        switch (key)
        {
            case 1: EXPECT_EQ( "a", value ); break;
            case 2: EXPECT_EQ( "b", value ); break;
            case 3: EXPECT_EQ( "c", value ); break;
            default: break;            
        };
    }
}

For me, the best examples come when combining features – the range-based for loop with structured bindings is a thing of beauty.

See also next C++17 post.

2 Comments

Filed under C++, C++ Code, Programming

C++17: if initialiser

I attended a C++17 presentation by Nicolai Josuttis last year, but at the time, my laptop’s compiler didn’t support any of the features to try them out. After a recent update, it turns out that many are now supported, so I’ve written a few unit tests using GTest.

The first feature I tried was the if initialiser. This feature looks a bit odd at first, because C++ programmers are so conditioned to seeing if statements containing a single condition. Allowing an initialiser statement as well

if ( initialiser; condition )

means that you can initialise a variable and test it on the same line. It also prevents the variable being used outside the scope of the if statement – this prevents accidental re-use if you subsequently mis-type a variable name.

auto return_int()
{
   return 101;
}

TEST( Cpp17, if_initialiser )
{
    // NB we can use i in the body of the if or the else
    // Also, must have a variable name for the object to live in the whole statement
    // (so must name locks taken, even if not used in the body, otherwise it's a temporary).
    if ( auto i = return_int(); i < 100 )
    {
        EXPECT_TRUE( i < 100 );
    }
    else
    {
        EXPECT_TRUE( i >= 100 );
    }
}

TEST( Cpp17, if_initialiser_with_map_insert)
{
    std::map<int, std::string> my_map{ {42, "Hi" } };
    
    if ( auto[it, inserted] = my_map.insert( std::make_pair(42, "Bye" ) ); !inserted )
    {
        // See also StructuredBindings for iterating over a map
        auto& [key,value] = *it; // iterator is pair of key and value
        EXPECT_EQ( 42, key );
        EXPECT_EQ( "Hi", value );
        
        value = "Bye"; // update the value
        EXPECT_EQ( "Bye", my_map[42] );
        
        // key = 43; // compile error! - cannot assign to const key-type
    }
}

See also next post on C++17 Structured Bindings.

1 Comment

Filed under C++, C++ Code, Programming

C++17 News

Herb Sutter blogged about the Oulo ISO C++ meeting. There’s a link to an interview he did for the excellent CppCast podcast and also to a reddit post on features approved at the meeting.
CppCast

As Herb called out in the interview, I think the following two features will be widely used:
Structured Bindings
This is interesting because I’ve been using the std::tie syntax for a while, despite the clumsiness when you need to introduce new variables to take the return types (leaving them uninitialised until assignment). The proposal above avoids that problem.

tuple<T1,T2,T3> f(/*...*/) { /*...*/ return {a,b,c}; }

// BEFORE
T1 x;
T2 y;
T3 z;
std::tie( x, y, z ) = f();

// AFTER
auto [x,y,z] = f(); // x has type T1, y has type T2, z has type T3

Initialisation clause for if and switch
This proposal makes if/switch more consistent with for loops that allow separate initialisation/increment clauses as well as a condition. So now you can initialise a variable separately from the if condition, which also allows you to limit the scope of that variable.

// BEFORE
status_code c = bar();     
if (c != SUCCESS) 
{       
    return c;     
}

// AFTER
if (status_code c = bar(); c != SUCCESS) 
{     
  return c;   
}

Leave a comment

Filed under C++, Programming

C++: std::make_array (N4315)

I read in May 2015’s CVU that there’s a proposal for std::make_array, a utility method in the same family as std::make_tuple and std::make_pair.

This would be a useful shorthand:

// Existing usage
std::array<double, 3> a = { 1.1, 2.2, 3.3 };

// Proposed usage
auto a = std::make_array( 1.1, 2.2, 3.3 };

The obvious benefit is that you would no longer need to specify the number of elements in the array, making the code more maintainable.

Leave a comment

Filed under C++, C++ Code