C++ structured binding

考虑对一个tuple的读取,如下代码:

int a;
char b;
double c;
std::tuple<int,char,double> tp = std::make_tuple(1, 'a', 2.3);
a = std::get<0>(tp); 
b = std::get<1>(tp);
c = std::get<2>(tp);

如果你认为tuple用处没有pair大,则有pair的代码:

int x;
bool y;
std::pair<int, bool> pp = std::make_pair(100,true);
x = pp.first;  
x = std::get<0>(pp);
y = pp.second;
y = std::get<1>(pp);

C++11给出了简洁的方式,如下代码:

int x;
bool y;
std::tie(x,y) = std::make_pair(100,true);

C++17给出更简洁的方式,如下代码:

auto[x,y] = std::make_pair(100,true);

这就是结构化的绑定(SB)的第二种形式。SB可以绑定数组的元素(第一种形式),SB绑定结构体的数据成员(第三种形式)。

有啥用呢?就是简化代码!方便程序员写代码。

考虑最早的循环遍历一个map,代码如下:

std::map<int, std::string> mymap{ {1,"hello"},{2," "},{3,"world"} };
std::map<int, std::string>::iterator ite_beg = mymap.begin();
std::map<int, std::string>::iterator ite_end = mymap.end();
for ( ; ite_beg != ite_end; ++ite_beg) {
    std::pair<int, std::string>& pp = *ite_beg;
    std::cout << pp.first << " " << pp.second << std::endl;
}

看着很高大上的,能蒙一下不懂技术的老板多掏钱给你。其实,这段代码很low,就是写个迭代器类型,拷贝粘贴就完成了。

C++11 引入auto后,可以不用迭代器的类型了,代码如下:

std::map<int, std::string> mymap{ { 1,"hello" },{ 2," " },{ 3,"world" } };
auto ite_beg = mymap.begin();
auto ite_end = mymap.end();
for (; ite_beg != ite_end; ++ite_beg) {
    auto& pp = *ite_beg;
    std::cout << pp.first << " " << pp.second << std::endl;
}

C++11引入新式的for循环语法后,可以让编译器替你展开这种繁文缛节的定式代码。

std::map<int, std::string> mymap{ { 1,"hello" },{ 2," " },{ 3,"world" } };
for ( auto& pp : mymap) {
    std::cout << pp.first << " " << pp.second << std::endl;
}

一切都很嗨,就是pp.first,pp.second很不爽,这时候,结构化绑定就来帮忙了。代码如下:

std::map<int, std::string> mymap{ { 1,"hello" },{ 2," " },{ 3,"world" } };
for ( auto& pp : mymap) {
    auto[a,b] = pp;
    std::cout << a << " " << b << std::endl;
}

C++17看来,pp也是很多余的,进一步简化代码如下:

for ( auto[a,b] : mymap) {
    std::cout << a << " " << b << std::endl;
}

结构化绑定支持自定义类,但是需要自定义类暴露公有成员。考虑一个有继承的情况:

#include <vector>
#include <string>

class Config_Base {
    std::string name;
    std::size_t id;
    std::vector<std::string> data;
public:
    auto get_name(){
        return std::string_view(name);
    }
    auto get_id(){
        return id;
    }
    auto get_data(){
        return (data);
    }
};

class Config : private Config_Base{

};

int main(){

    Config cfg;
    auto& [a,b,c] = cfg;  //编译失败,原因:私有成员看不到呀。
}

我们需要改造一下,骗过编译器。让我们的自定义类的行为好像是tuple一般。怎么骗呢?代码如下:

#include <vector>
#include <string>
#include <iostream>

class Config_Base {
    std::string name="hello";
    std::size_t id=123;
    std::vector<std::string> data;
public:
    auto get_name() const{
        return std::string_view(name);
    }
    auto get_id() const{
        return id;
    }
    auto get_data() const{
        return (data);  //括起来,就是返回引用,很神奇。
    }
};

class Config : private Config_Base{
    //...
public:
   template <std::size_t N>
   decltype(auto) get() const {
       if      constexpr (N == 0) return get_name();
       else if constexpr (N == 1) return get_id();
       else if constexpr (N == 2) return get_data();
   }
};

namespace std {

    template<>
    struct tuple_size<Config>
        : std::integral_constant<std::size_t, 3> {};

    /*等价于
    template<>
    struct tuple_size<Config>{ static const int value = 3; };
    */
}

namespace std {

    template<std::size_t N>
    struct tuple_element<N, Config> {
        using type = decltype(std::declval<Config>().get<N>());
    };

    /*等价于
    template<> struct tuple_element<0,Config> { using type = std::string_view; };
    template<> struct tuple_element<1,Config> { using type = std::size_t; };
    template<> struct tuple_element<2,Config> { using type = const std::vector<std::string>&; };
    */
}

int main(){

    Config cfg;
    auto& [a,b,c] = cfg;

    std::cout << a << std::endl;
}
结构化绑定易错陷阱,考虑如下代码:
1 #include <iostream>
 2 
 3 struct S { 
 4     int i = 0; 
 5     ~S(){ std::cout << "~S() i=" << i << std::endl; }
 6 };
 7 
 8 S makeS(){
 9    S s;
10    return s;   
11 }
12 
13 int main()
14 {
15    auto& [ d ] = makeS();
16    d++;
17 }

第15行编译失败。用伪代码展开,可以看到:

1 auto & __tmp = makeS();
2 int& d = __tmp.i;

第1行的auto&是程序员输入的,显然是错误的。因为左值引用不能抓右值。

第2行的int&是编译器设定的(也是语言规范要求的),i是对__tmp.i的引用。

参考: https://github.com/tvaneerd/cpp17_in_TTs/blob/master/ALL_IN_ONE.md
https://blog.tartanllama.xyz/structured-bindings/

原文链接: https://www.cnblogs.com/thomas76/p/8522639.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/269990

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月14日 下午8:52
下一篇 2023年2月14日 下午8:52

相关推荐