Tuple Unpacking (Err, Structured Binding)¶
Basics¶
Wanted: Python’s tuple unpacking
a, b, c = [1, 2, 3] # from list
a, b, c = (1, 2, 3) # from tuple
...
C++-ification of Python
Comes in many flavors since C++ cares more about types, obviously
Creates variables in surrounding scope
For example …
struct point
{
int x, y;
};
point p{1,2};
auto [x, y] = p;
Variations, Compatible Data Types¶
Variant |
Meaning |
---|---|
|
By copy |
|
By reference |
|
By |
Works on structured data
struct
std::tuple
std::pair
…
Works on arrays too
int array[] {1, 2, 3};
auto [x, y, z] = array;
Not on pointer to array;
int array[] {1, 2, 3};
int *parray = array;
auto [x, y, z] = parray;
error: cannot decompose non-array non-class type ‘int*’
25 | const auto& [x, y, z] = array;
| ^~~~~~~~~
struct
: By Copy¶
#include <gtest/gtest.h>
struct point
{
int x, y;
};
TEST(structured_binding_suite, struct_copy)
{
point p{1,2};
auto [x, y] = p;
ASSERT_EQ(x, p.x);
ASSERT_EQ(y, p.y);
// copy
x = y = 666;
ASSERT_NE(p.x, 666);
ASSERT_NE(p.y, 666);
}
struct
: Reference¶
#include <gtest/gtest.h>
struct point
{
int x, y;
};
TEST(structured_binding_suite, struct_reference)
{
point p{1,2};
auto& [x, y] = p;
ASSERT_EQ(x, p.x);
ASSERT_EQ(y, p.y);
// x, y are *references*, so assignment to those assigns to the
// *original* struct members
x = y = 666;
ASSERT_EQ(p.x, 666);
ASSERT_EQ(p.y, 666);
}
struct
: By const
Reference¶
#include <gtest/gtest.h>
struct point
{
int x, y;
};
TEST(structured_binding_suite, struct_const_reference)
{
point p{1,2};
const auto& [x, y] = p;
ASSERT_EQ(x, p.x);
ASSERT_EQ(y, p.y);
// x, y are *references*, so addresses must match
ASSERT_EQ(&x, &p.x);
ASSERT_EQ(&y, &p.y);
// const reference -> does not compile
// x = y = 666;
}
std::tuple
: By Copy¶
#include <gtest/gtest.h>
TEST(structured_binding_suite, tuple_copy)
{
auto t = std::make_tuple(1, 2, 3);
auto [x, y, z] = t;
ASSERT_EQ(x, std::get<0>(t));
ASSERT_EQ(y, std::get<1>(t));
ASSERT_EQ(z, std::get<2>(t));
// x, y, z are *copies*, so original tuple is *not* affected by
// assignment
x = y = z = 666;
ASSERT_NE(std::get<0>(t), 666);
ASSERT_NE(std::get<1>(t), 666);
ASSERT_NE(std::get<2>(t), 666);
}
std::tuple
: By Reference¶
#include <gtest/gtest.h>
TEST(structured_binding_suite, tuple_reference)
{
auto t = std::make_tuple(1, 2, 3);
auto& [x, y, z] = t;
ASSERT_EQ(x, std::get<0>(t));
ASSERT_EQ(y, std::get<1>(t));
ASSERT_EQ(z, std::get<2>(t));
// x, y, z are *reference*, so assignment to those goes into the
// *original* tuple members
x = y = z = 666;
ASSERT_EQ(std::get<0>(t), 666);
ASSERT_EQ(std::get<1>(t), 666);
ASSERT_EQ(std::get<2>(t), 666);
}
std::tuple
: By const
Reference¶
#include <gtest/gtest.h>
TEST(structured_binding_suite, tuple_const_reference)
{
auto t = std::make_tuple(1, 2, 3);
const auto& [x, y, z] = t;
ASSERT_EQ(x, std::get<0>(t));
ASSERT_EQ(y, std::get<1>(t));
ASSERT_EQ(z, std::get<2>(t));
// x, y, z are *const references*, so compiler doesn't let me
// assign ("assignment of read-only reference")
// x = y = z = 666; <--- does not compile
// but lets make sure that x, y, z are still *references* -> the
// addresses must match
ASSERT_EQ(&x, &std::get<0>(t));
ASSERT_EQ(&y, &std::get<1>(t));
ASSERT_EQ(&z, &std::get<2>(t));
}
Arrays: By Copy¶
#include <gtest/gtest.h>
TEST(structured_binding_suite, array_copy)
{
int array[] {1, 2, 3};
auto [x, y, z] = array;
ASSERT_EQ(x, array[0]);
ASSERT_EQ(y, array[1]);
ASSERT_EQ(z, array[2]);
// x, y, z are *copies*, so assignment does not go through to the
// originals
x = y = z = 666;
ASSERT_NE(array[0], 666);
ASSERT_NE(array[1], 666);
ASSERT_NE(array[2], 666);
}
Arrays: By Reference¶
#include <gtest/gtest.h>
TEST(structured_binding_suite, array_reference)
{
int array[] {1, 2, 3};
auto& [x, y, z] = array;
ASSERT_EQ(x, array[0]);
ASSERT_EQ(y, array[1]);
ASSERT_EQ(z, array[2]);
// x, y, z are *references*, so assignment to those goes though
// right into the original array members
x = y = z = 666;
ASSERT_EQ(array[0], 666);
ASSERT_EQ(array[1], 666);
ASSERT_EQ(array[2], 666);
}
Arrays: By const
Reference¶
#include <gtest/gtest.h>
TEST(structured_binding_suite, array_const_reference)
{
int array[] {1, 2, 3};
const auto& [x, y, z] = array;
ASSERT_EQ(x, array[0]);
ASSERT_EQ(y, array[1]);
ASSERT_EQ(z, array[2]);
// x, y, z are *const references*, so the compiler won't let me
// assign ("error: assignment of read-only variable")
// x = y = z = 666; <--- does not compile
// but lets make sure that x, y, z are still *references* (not
// that the compiler error message says "assignment of read-only
// **variable**" which is suspicious). lets make sure that we
// really talk about references:
ASSERT_EQ(&x, &array[0]);
ASSERT_EQ(&y, &array[1]);
ASSERT_EQ(&z, &array[2]);
}