我想知道在C ++ 0x的标准库中如何实现元组。我试着阅读description in libstdc++ manual,然后阅读template listing,但很难理解它是如何工作的,特别是在阅读代码时。
有人能用几句话解释我的元组实现的想法吗?我想知道这一点,因为我想在我的代码中使用元组,我想了解它是如何工作的以及它带来了什么类型的开销(仅扩展编译时间,对内存执行许多复制操作,在构造函数中执行许多其他函数)等等。)。
答案 0 :(得分:25)
实现元组的一种方法是使用多重继承。元组元素由叶类保存,元组类本身继承自多个叶子。在伪代码中:
template<typename T0, typename T1, ..., typename Tn>
class PseudoTuple : TupleLeaf<0, T0>, TupleLeaf<1, T1>, ..., TupleLeaf<n, Tn> {
...
};
每个叶子都有一个索引,因此即使它们包含的类型相同,每个基类也会变得唯一,所以我们可以通过简单的static_cast访问 nth 元素:
static_cast<TupleLeaf<0, T0>*>(this);
// ...
static_cast<TupleLeaf<n, Tn>*>(this);
我在这里写了关于这个“扁平”元组实现的详细解释:C++11 tuple implementation details (Part 1)
答案 1 :(得分:10)
元组通常实现为编译时链表。
代码通过模板语法有点混淆,但通常会出现以下元素:
在C ++ 03中存在合理的实现(例如,boost)。
Variadic模板允许无限数量的元素,如Motti所述。
成本通常是编译时间一。复制构造函数可能在初始化期间被调用(最大1),以及复制元组本身时。
答案 2 :(得分:5)
我想我会基于@mitchnull答案添加一个非伪代码的简单实现,以供参考
#include <iostream>
// Contains the actual value for one item in the tuple. The
// template parameter `i` allows the
// `Get` function to find the value in O(1) time
template<std::size_t i, typename Item>
struct TupleLeaf {
Item value;
};
// TupleImpl is a proxy for the final class that has an extra
// template parameter `i`.
template<std::size_t i, typename... Items>
struct TupleImpl;
// Base case: empty tuple
template<std::size_t i>
struct TupleImpl<i>{};
// Recursive specialization
template<std::size_t i, typename HeadItem, typename... TailItems>
struct TupleImpl<i, HeadItem, TailItems...> :
public TupleLeaf<i, HeadItem>, // This adds a `value` member of type HeadItem
public TupleImpl<i + 1, TailItems...> // This recurses
{};
// Obtain a reference to i-th item in a tuple
template<std::size_t i, typename HeadItem, typename... TailItems>
HeadItem& Get(TupleImpl<i, HeadItem, TailItems...>& tuple) {
// Fully qualified name for the member, to find the right one
// (they are all called `value`).
return tuple.TupleLeaf<i, HeadItem>::value;
}
// Templated alias to avoid having to specify `i = 0`
template<typename... Items>
using Tuple = TupleImpl<0, Items...>;
int main(int argc, char** argv) {
Tuple<int, float, std::string> tuple;
Get<0>(tuple) = 5;
Get<1>(tuple) = 8.3;
Get<2>(tuple) = "Foo";
std::cout << Get<0>(tuple) << std::endl;
std::cout << Get<1>(tuple) << std::endl;
std::cout << Get<2>(tuple) << std::endl;
return 0;
}
答案 3 :(得分:3)
可以通过引入核心语言的variadic templates实现std::tuple
。
我知道这是在乞求这个问题,但它为你提供了一个更好的搜索词。
答案 4 :(得分:1)
通过组合而不是继承使用递归数据结构的实现:
#include <iostream>
template <typename T, typename... Ts>
struct Tuple {
Tuple(const T& t, const Ts&... ts)
: value(t)
, rest(ts...)
{
}
constexpr int size() const { return 1 + rest.size(); }
T value;
Tuple<Ts...> rest;
};
template <typename T>
struct Tuple<T> {
Tuple(const T& t)
: value(t)
{
}
constexpr int size() const { return 1; }
T value;
};
template <size_t i, typename T, typename... Ts>
struct nthType : nthType<i-1, Ts...> {
static_assert(i < sizeof...(Ts) + 1, "index out of bounds");
};
template <typename T, typename... Ts>
struct nthType<0, T, Ts...> { T value; };
template <size_t i>
struct getter {
template <typename... Ts>
static decltype(nthType<i, Ts...>::value)& get(Tuple<Ts...>& t) {
return getter<i-1>::get(t.rest);
}
};
template <>
struct getter<0> {
template <typename T, typename... Ts>
static T& get(Tuple<T, Ts...>& t) {
return t.value;
}
};
template <size_t i, typename... Ts>
decltype(nthType<i, Ts...>::value)& get(Tuple<Ts...>& t) {
return getter<i>::get(t);
}
int main()
{
Tuple<int,int,float> t(1,2,3.4);
std::cout << get<0>(t) << "\n";
std::cout << get<1>(t) << "\n";
std::cout << get<2>(t) << "\n";
// std::cout << get<3>(t) << "\n"; // error with useful information
return 0;
}
我发现这种方法远远优于替代方法,因为它在执行 apply
map
等递归操作时使用起来非常直观,特别是如果您曾经在函数式编程中使用过递归数据结构。当然,对于索引检索,我们需要做一些奇怪的模板事情,但通常使用递归性质是非常直观的。如果有人能解释为什么这种设置不常见,我很想知道。