基本用法
structured bindings让我们能通过tuple,std::pair或是没有静态数据成员的结构体来初始化变量。在C++17之前如果要解包一个结构体时,我们需要借助tie,像这样:
auto tp = std::make_tuple(1, 2.5, 'c'); int a; double b; char c; std::tie(a, b, c) = tp;
现在有了C++17的structured bindings,我们可以很方便地解包tuple了,像这样。
auto tp = std::make_tuple(1, 2.5, 'c'); auto [a, b, c] = tp;
遍历map的时候也不用像以前一样写pair然后it->first, it->second了,而是用更加简洁的方式,像这样:
std::map<int, int> mp = { {1, 2}, {3, 4} }; for(auto&& [k, v] : mp) std::cout<<"key: "<<k<<" "<<"value: "<<v<<'\n';
更酷的是你可以用来解包一个结构体。
struct Foo { int i; char c; std::tuple<double> d; std::map<int, int> e; }; std::tuple<double> tp(2.3); Foo f { 1, 'a', tp, {{1,2}} }; auto& [i,c,d, e] = f; std::cout<<i<<" "<<c<<" "<<e[1]<<'\n'; i = 2; c = 'b';
需要注意的是,你必须提供和结构体或tuple字段个数相同的变量,否则会出现编译错误,而且你不能像std::ignore一样忽略某些字段,必须全部解包出来。解包的时候你可以选择引用或拷贝,auto&就是引用方式解包。
特殊用法
我们可以借助这个特性来做一点有趣的事,比如把一个结构体变成一个tuple。
template <class T, class... TArgs> decltype(void(T{std::declval<TArgs>()...}), std::true_type{}) test_is_braces_constructible(int); template <class, class...> std::false_type test_is_braces_constructible(...); template <class T, class... TArgs> using is_braces_constructible = decltype(test_is_braces_constructible<T, TArgs...>(0)); struct any_type { template<class T> constexpr operator T(); // non explicit }; template<class T> auto to_tuple(T&& object) noexcept { using type = std::decay_t<T>; if constexpr(is_braces_constructible<type, any_type, any_type, any_type, any_type>{}) { auto&& [p1, p2, p3, p4] = object; return std::make_tuple(p1, p2, p3, p4); } else if constexpr(is_braces_constructible<type, any_type, any_type, any_type>{}) { auto&& [p1, p2, p3] = object; return std::make_tuple(p1, p2, p3); } else if constexpr(is_braces_constructible<type, any_type, any_type>{}) { auto&& [p1, p2] = object; return std::make_tuple(p1, p2); } else if constexpr(is_braces_constructible<type, any_type>{}) { auto&& [p1] = object; return std::make_tuple(p1); } else { return std::make_tuple(); } } //test code struct s { int p1; double p2; int mp3[2]; std::map<std::string, int> v4; }; std::map<std::string, int> v = {{"a", 2}}; auto t = to_tuple(s{1, 2.0, {2,3}, v});
实现的思路很巧妙,先判断某个类型是否由指定个数的参数构造,isbracesconstructible<type, any_type>{}就是判断type是否能由一个参数构造;接着通过constexpr if在编译期选择有效的分支,其它的分支将会被丢弃;最后通过structured bindings来获取结构体字段的值,并将这些值重新构造一个tuple,这样就可以实现将结构体转换为tuple了。上面的例子中由于有四个字段所以会走第一个分支。需要注意的是,这个方法只是一个示例,还没解决构造函数被delete的问题,还有隐式转换的问题,如果你想看更完整的解决方案可以看这里:http://playfulprogramming.blogspot.hk/2016/12/serializing-structs-with-c17-structured.html。
structured bindings具备解包结构体的能力,虽然限制条件也不少,我相信它的用法值得深入探索。




