Author Topic: Structured Bindings  (Read 123 times)

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4605
Structured Bindings
« on: December 04, 2018, 03:52:19 AM »
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:
Code: cpp [Select]

#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.
Code: cpp [Select]

  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:
Code: cpp [Select]

  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:
Code: cpp [Select]

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)