C++17 Practical examples of structured bindings

Structured bindings are one of the most significant additions to C++17 in terms of user experience. They are very handy for situations which require iteration over std::map. They are a great improvement compared to the previously used iterator access it->first, it->second.

int main(){
    const std::map<std::string, std::string> countryCodes {
        { "Switzerland", "41"},
        { "Italy", "39"},
        { "France", "33"},
        { "UK", "44"},
    };
    
    for (const auto & [k,v] : countryCodes) 
        std::cout << k << " " << v << "\n";
}

This example demonstrates how to modify coordinates of points stored in std::vector with the help of structured bindings. This approach works only for structs/classes with public data members.

struct Point {
    Point():x(0.0), y(0.0){}
    Point(double xx, double yy):x(xx), y(yy){}
    double x;
    double y;
};

int main(){
    std::vector<Point> points = { 
                             Point(1.2, 2.1),
                             Point(3.1, 4.3),
                             Point(5.8, 1.0)};
    
    for(auto & [x,y] : points)
        std::cout << ++x << ", " << ++y << '\n';
}

This example demonstrates the use of structured bindings as an alias for the return value of a function which converts cartesian coordinates to polar coordinates.

auto toPolar(std::valarray<double> const & v){
    return std::make_pair(sqrt(v[0]*v[0]+v[1]*v[1]),
                          atan(v[1]/v[0]));
}

int main(){
    std::valarray<double> v = {3.0,4.0};
    auto [rho,theta] = toPolar(v);
    std::cout << "Distance to origin: " << rho << '\n'; // 5
    std::cout << "Angle in radians: " << theta << '\n'; // 0.927295
}

The fourth example demonstrates the use of structured bindings as an alias for the return value of a function returning iterators to the minimum and maximum element of a vector.

int main(){
    std::vector v = {7,8,4,3};
    auto [a,b] = std::minmax_element(std::begin(v),std::end(v));
    std::cout << "min: " << *a << " max: " << *b << "\n"; // min: 3 max: 8
}

The last example shows what to do in case you plan to omit some of the bound values. In this case, we are only interested in the second argument, i.e. whether the actual emplacement took place, denoted as 'ins'. We are not interested in the iterator. This can be accomplished via [[maybe_unused]]. Unfortunately, as of now [[maybe_unused]] has to be placed in front of the return value and cannot be placed directly in front of the value that we intend to omit.

int main(){
    std::map m = {{2, "two"}, {7, "seven"}};
    [[maybe_unused]] auto [it,ins] = m.try_emplace(5, "five");
    std::cout << ins << '\n';
}