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