std :: get如何工作?

Me *_*d I 5 c++ templates tuples c++11 stdtuple

在尝试自己创建一个std::get<N>(std::tuple)方法后,我不太确定它是如何由编译器实现的.我知道std::tuple有这样的构造函数,

tuple(Args&&... args);
Run Code Online (Sandbox Code Playgroud)

但到底args...分配给了什么?我认为这对于知道如何std::get工作是有用的,因为需要将参数放在某处以便访问它们...

Yak*_*ont 8

这是一个tuple类似于类的粗略玩具实现.

首先,一些元编程样板,表示一个整数序列:

template<int...> struct seq {};
template<int max, int... s> struct make_seq:make_seq< max-1, max-1, s... > {};
template<int... s> struct make_seq<0, s...> {
  typedef seq<s...> type;
};
template<int max> using MakeSeq = typename make_seq<max>::type;
Run Code Online (Sandbox Code Playgroud)

接下来,实际存储数据的标记类:

template<int x, typename Arg>
struct foo_storage {
  Arg data;
};
Run Code Online (Sandbox Code Playgroud)

每当我们想要在编译时将数据与某个标记相关联时(这种情况下,整数),这种标记技术就是一种常见的模式.标签(int此处)通常不用于存储中的任何位置,它仅用于标记存储.

foo_helper将一个序列和一组参数解包成一堆foo_storage,并以线性方式从它们继承.这是一种非常常见的模式 - 如果你这么做,你最终会创建为你做这个的元编程工具:

template<typename Seq, typename... Args>
struct foo_helper {};
template<int s0, int... s, typename A0, typename... Args>
struct foo_helper<seq<s0, s...>, A0, Args...>:
  foo_storage<s0, A0>,
  foo_helper<seq<s...>, Args...>
{};
Run Code Online (Sandbox Code Playgroud)

我的粗tuple类型foo创建了一系列索引和args的包,并将其传递给上面的帮助器.然后帮助程序创建一组包含父类的标记数据:

template<typename... Args>
struct foo: foo_helper< MakeSeq<sizeof...(Args)>, Args... > {};
Run Code Online (Sandbox Code Playgroud)

我删除了所有内容foo,因为它不需要实现get.

get非常简单:我们采用存储类型(不是元组类型),显式template参数N消除了foo_storage<n, T>我们要访问的内容的歧义.现在我们有了存储类型,我们只需返回数据字段:

template<int N, typename T>
T& get( foo_storage<N, T>& f )
 { return f.data; }
template<int N, typename T>
T const& get( foo_storage<N, T> const& f )
 { return f.data; }
Run Code Online (Sandbox Code Playgroud)

我们正在使用C++语言的重载机制来完成繁重的工作.当您使用类实例调用函数时,该实例作为每个父类都会被查看,以查看是否可以使它们中的任何一个匹配.使用Nfixed时,只有一个父类是有效参数,因此父类(因此T)会自动推导出来.

最后,一些基本的测试代码:

#include <iostream>

int main() {
  foo<int, double> f;
  get<0>( f ) = 7;
  get<1>( f ) = 3.14;
  std::cout << get<0>(f) << "," << get<1>(f) << "\n";
}
Run Code Online (Sandbox Code Playgroud)

  • 这可以大大改进.例如,参见[这个玩具实现](http://coliru.stacked-crooked.com/view?id=d51ff6c809c9d6fabede11d0fa67a19a-f0d9bbac4ab033ac5f4ce440d21735ee)我前段时间写过.它依赖于派生到基础的转换,并通过指定基本类型的一部分(即索引)来强制正确的转换. (3认同)
  • @Xeo 已修复,删除了 `int(*)[N]` hack。 (2认同)