C++17特性:结构化绑定(四)自定义API支持结构化绑定

论坛 期权论坛 期权     
软件开发谈   2019-7-29 08:48   4740   0
通过提供类Tuple的API,可以为任意类型实现结构化绑定支持,就像标准库中std::pairstd::tuplestd::array所做的那样。
  • 开启只读的结构化绑定
下面的例子演示了如何使类型Customer支持结构化绑定:
lang/customer1.hpp
  1. #include
复制代码
  1. #include  // for std::move()
复制代码
  1. class Customer {
复制代码
  1. private:
复制代码
  1.   std::string first;
复制代码
  1.   std::string last;
复制代码
  1.   long val;
复制代码
  1. public:
复制代码
  1.   Customer (std::string f, std::string l, long v)
复制代码
  1.   : first{std::move(f)}, last{std::move(l)}, val{v} {
复制代码
  1.   }
复制代码
  1.   std::string getFirst() const {
复制代码
  1.     return first;
复制代码
  1.   }
复制代码
  1.   std::string getLast() const {
复制代码
  1.     return last;
复制代码
  1.   }
复制代码
  1.   long getValue() const {
复制代码
  1.     return val;
复制代码
  1.   }
复制代码
  1. };
复制代码
可以实现类Tuple API如下:
lang/structbind1.hpp
  1. #include "customer1.hpp"
复制代码
  1. #include  // for tuple-like API
复制代码
  1. // 为类型Customer实现类Tuple API以支持结构化绑定:
复制代码
  1. template
复制代码
  1. struct std::tuple_size {
复制代码
  1.   static constexpr int value = 3; // 有 3 个属性(成员变量)
复制代码
  1. };
复制代码
  1. template
复制代码
  1. struct std::tuple_element {
复制代码
  1.   using type = long; // 最后一个属性类型是long
复制代码
  1. };
复制代码
  1. template
复制代码
  1. struct std::tuple_element {
复制代码
  1.   using type = std::string; // 其他属性类型都是strings
复制代码
  1. };
复制代码
  1. // 定义getter:
复制代码
  1. template auto get(const Customer& c);
复制代码
  1. template auto get(const Customer& c) { return c.getFirst(); }
复制代码
  1. template auto get(const Customer& c) { return c.getLast(); }
复制代码
  1. template auto get(const Customer& c) { return c.getValue(); }
复制代码
这里我们为Customer的3个属性(成员变量)定义类Tuple API,因此可以很容易地把3个getter映射到Customer上(任何其他用户定义的映射都是可以的):
  • 第一个名字的类型是std::string
  • 最后一个名字的类型是std::string
  • 其他值类型是long
属性个数通过为Customer类型特化std::tuple_size来定义:
  1. template
复制代码
  1. struct std::tuple_size {
复制代码
  1.   static constexpr int value = 3; // 有3 个属性
复制代码
  1. };
复制代码
通过特化std::tuple_element来定义属性类型:
  1. template
复制代码
  1. struct std::tuple_element {
复制代码
  1.   using type = long; // 最后一个属性是 long
复制代码
  1. };
复制代码
  1. template
复制代码
  1. struct std::tuple_element {
复制代码
  1.   using type = std::string; // 其他属性是string
复制代码
  1. };
复制代码
第3个属性类型是long,由全特化index 2指定。其他属性通过偏特化(优先级比全特化低)指定类型为std::string。这里指定的类型,跟结构化绑定decltype得到的类型一致。
最后,通过在同一名字空间内为类型Customer重载get()函数,就可以定义对应的getter:
  1. template auto get(const Customer& c);
复制代码
  1. template auto get(const Customer& c) { return c.getFirst(); }
复制代码
  1. template auto get(const Customer& c) { return c.getLast(); }
复制代码
  1. template auto get(const Customer& c) { return c.getValue(); }
复制代码
在这个示例中,我们用到了主函数模板声明和全特化。
要注意的是,所有函数模板的全特化必须使用相同的函数签名(包括确切的返回值类型)。理由是我们只提供了特定的“实现”,没有新的声明。下面的代码就编译不过:
  1. template auto get(const Customer& c);
复制代码
  1. template std::string get(const Customer& c) { return c.getFirst(); }
复制代码
  1. template std::string get(const Customer& c) { return c.getLast(); }
复制代码
  1. template long get(const Customer& c) { return c.getValue(); }
复制代码
使用编译期if特性,可以将get()实现合并到一个函数中:
  1. template auto get(const Customer& c) {
复制代码
  1.   static_assert(I < 3);
复制代码
  1.   if constexpr (I == 0) {
复制代码
  1.     return c.getFirst();
复制代码
  1.   }
复制代码
  1.   else if constexpr (I == 1) {
复制代码
  1.     return c.getLast();
复制代码
  1.   }
复制代码
  1.   else { // I == 2
复制代码
  1.     return c.getValue();
复制代码
  1.   }
复制代码
  1. }
复制代码
有了这个API,我们就可以像下面这样使用Customer的结构化绑定:
lang/structbind1.cpp
  1. #include "structbind1.hpp"
复制代码
  1. #include
复制代码
  1. int main()
复制代码
  1. {
复制代码
  1.   Customer c{"Tim", "Starr", 42};
复制代码
  1.   auto [f, l, v] = c;
复制代码
[code]  std::cout
分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:10
帖子:2
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP