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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值