C++11 标记元组

2022-01-20 00:00:00 tuples c++ c++11

C++11 元组很好,但它们对我来说有两个巨大的缺点,通过索引访问成员是

C++11 tuples are nice, but they have two huge disadvantages to me, accessing members by index is

  1. 不可读
  2. 难以维护(如果我在元组中间添加一个元素,我就完蛋了)

本质上我想要实现的是这个

In essence what I want to achieve is this

tagged_tuple <name, std::string, age, int, email, std::string> get_record (); {/*...*/}
// And then soomewhere else

std::cout << "Age: " << get_record().get <age> () << std::endl;

在 boost::property_map 中实现了类似的东西(类型标记),但我不知道如何在具有任意数量元素的元组中实现它

Something similar (type tagging) is implemented in boost::property_map, but I ca'nt get my head around how to implement it in a tuple with arbitary number of elements

PS请不建议使用元组元素索引定义枚举.

PS Please do not suggest defining an enum with tuple element indices.

UPD好的,这是一个动机.在我的项目中,我需要能够即时"定义许多不同的元组,并且它们都需要具有某些通用函数和运算符.这是结构无法实现的

UPD OK, here is a motivation. In my projects I need to be able to define lots of different tuples 'on-the-fly' and all of them need to have certain common functions and operators. This is not possible to achieve with structs

UPD2实际上,我的示例实施起来可能有点不切实际.这个怎么样?

UPD2 Actually my example is probably a bit unrealistic to implement. How about this?

tagged_tuple <tag<name, std::string>, tag<age, int>, tag<email, std::string>> get_record (); {/*...*/}
// And then somewhere else

std::cout << "Age: " << get_record().get <age> () << std::endl;

推荐答案

我不知道有任何现有的类这样做,但是使用 std::tuple 和一个索引类型列表:

I'm not aware of any existing class that does this, but it's fairly easy to throw something together using a std::tuple and an indexing typelist:

#include <tuple>
#include <iostream>

template<typename... Ts> struct typelist {
  template<typename T> using prepend = typelist<T, Ts...>;
};

template<typename T, typename... Ts> struct index;
template<typename T, typename... Ts> struct index<T, T, Ts...>:
  std::integral_constant<int, 0> {};
template<typename T, typename U, typename... Ts> struct index<T, U, Ts...>:
  std::integral_constant<int, index<T, Ts...>::value + 1> {};

template<int n, typename... Ts> struct nth_impl;
template<typename T, typename... Ts> struct nth_impl<0, T, Ts...> {
  using type = T; };
template<int n, typename T, typename... Ts> struct nth_impl<n, T, Ts...> {
  using type = typename nth_impl<n - 1, Ts...>::type; };
template<int n, typename... Ts> using nth = typename nth_impl<n, Ts...>::type;

template<int n, int m, typename... Ts> struct extract_impl;
template<int n, int m, typename T, typename... Ts>
struct extract_impl<n, m, T, Ts...>: extract_impl<n, m - 1, Ts...> {};
template<int n, typename T, typename... Ts>
struct extract_impl<n, 0, T, Ts...> { using types = typename
  extract_impl<n, n - 1, Ts...>::types::template prepend<T>; };
template<int n, int m> struct extract_impl<n, m> {
  using types = typelist<>; };
template<int n, int m, typename... Ts> using extract = typename
  extract_impl<n, m, Ts...>::types;

template<typename S, typename T> struct tt_impl;
template<typename... Ss, typename... Ts>
struct tt_impl<typelist<Ss...>, typelist<Ts...>>:
  public std::tuple<Ts...> {
  template<typename... Args> tt_impl(Args &&...args):
    std::tuple<Ts...>(std::forward<Args>(args)...) {}
  template<typename S> nth<index<S, Ss...>::value, Ts...> get() {
    return std::get<index<S, Ss...>::value>(*this); }
};
template<typename... Ts> struct tagged_tuple:
  tt_impl<extract<2, 0, Ts...>, extract<2, 1, Ts...>> {
  template<typename... Args> tagged_tuple(Args &&...args):
    tt_impl<extract<2, 0, Ts...>, extract<2, 1, Ts...>>(
      std::forward<Args>(args)...) {}
};

struct name {};
struct age {};
struct email {};

tagged_tuple<name, std::string, age, int, email, std::string> get_record() {
  return { "Bob", 32, "bob@bob.bob"};
}

int main() {
  std::cout << "Age: " << get_record().get<age>() << std::endl;
}

您可能希望在现有访问器之上编写 const 和右值 get 访问器.

You'll probably want to write const and rvalue get accessors on top of the existing one.

相关文章