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.
Pingback: C++17: if initialiser | musingstudio
Pingback: C++17: Nested Namespaces | musingstudio