It seems JavaScript and Ruby like destructuring has come to C++17 as
Structured Bindings. This provides shorthand notation to pull out elements of arrays, tuples, and structs, into new variable declarations.
Examples:
#include <iostream>
#include <tuple>
// Helper method
void printPairs(int v1, int v2) {
std::cout << "{" << v1 << ", " << v2 << "}" << std::endl;
}
void demoArrayStructuredBindings() {
int a[2] = {1, 2}; // Declare and initialize array
// Declare variables with structured binding
auto [a0, a1] = a; // Copy array values
auto& [ra0, ra1] = a; // Set reference to array values
printPairs(a0, a1); // {1, 2} (Print copies)
printPairs(ra0, ra1); // {1, 2} (Print references to originals)
// Update original array
a[0] = 2;
a[1] = 3;
printPairs(a0, a1); // {1, 2} (Copied values are unaffected)
printPairs(ra0, ra1); // {2, 3} (References reflect updates)
}
void demoTupleStructuredBindings() {
// Create tuple
auto tuple = std::make_tuple(1, 4);
// Declare variables using structured binding
auto [v0, v1] = tuple;
printPairs(v0, v1); // {1, 4}
}
void demoStructStructuredBindings() {
struct S {
int field1:4; // Bitfield's work too
int field2;
};
// Create a struct
S s{5, 6};
// Declare variables with structured binding
auto [s0, s1] = s;
printPairs(s0, s1); // {5, 6}
}
int main() {
demoArrayStructuredBindings();
demoTupleStructuredBindings();
demoStructStructuredBindings();
return 0;
}
The case of the bitfield reference is particularly interesting. When working with GCC, I see warnings if I try to set the reference value beyond what the bitfield can hold. That suggests it's able to keep track of the restricted range of the bitfield.
S s1{0, 16};
auto& [s1f1, s1f2] = s1;
s1f1 = 16; // Warning: Overflow
If I had instead tried to set a normal reference, it would have failed:
auto& r = s1.field1; // Error: Cannot bind bitfield
The closest I've been able to do, is bind to a temporary copy of the value stored in the bitfield. This converts to the underlying type, and so the bitfield restriction is lost. If the binding is an rvalue reference, you can modify the temporary. If you use an lvalue reference, it must be declared const for the binding to be allowed. In either case, the binding is to a temporary copy, and so changes to the original field are not reflected:
int&& r1 = s1.field1; // Ok, read s1.field into a temporary of type int (no bitfield restriction), and set r1 to point to the temporary
const int& r2 = s1.field1; // Ok, read s1.field into a temporary of type int (no bitfield restriction), and set r2 to point to the temporary (read only)