Walletfox.com

Practical examples of structured bindings (C++17)

Accessing contents of std::map

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";
}

Retrieve and modify user-defined elements stored in std::vector

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';
}

Cartesian-to-polar coordinates - structured bindings as an alias for std::pair

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
}

Min and max - structured bindings as an alias for std::pair

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
}

What to do about an unused argument

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';
}

Tagged: C++