暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

C++17中的structured bindings

purecpp 2017-06-13
671

基本用法

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具备解包结构体的能力,虽然限制条件也不少,我相信它的用法值得深入探索。


文章转载自purecpp,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论