std :: get如何工作?

时间:2013-06-18 20:40:35

标签: c++ templates c++11 tuples stdtuple

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

tuple(Args&&... args);

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

1 个答案:

答案 0 :(得分: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;

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

template<int x, typename Arg>
struct foo_storage {
  Arg data;
};

每当我们想要在编译时将数据与某个标记相关联时(这种情况下,整数),这种标记技术就是一种常见的模式。标签(此处为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...>
{};

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

template<typename... Args>
struct foo: foo_helper< MakeSeq<sizeof...(Args)>, Args... > {};

我从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; }

我们正在使用C ++语言的重载机制来完成繁重的工作。当您使用类实例调用函数时,该实例作为每个父类都会被查看,以查看是否可以使它们中的任何一个匹配。固定N后,只有一个父类是有效参数,因此父类(以及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";
}