在这里http://en.cppreference.com/w/cpp/utility/tuple/tuple_element给出了std :: tuple_element的可能实现.
template< std::size_t I, class T >
struct tuple_element;
// recursive case
template< std::size_t I, class Head, class... Tail >
struct tuple_element<I, std::tuple<Head, Tail...>>
: std::tuple_element<I-1, std::tuple<Tail...>> { };
// base case
template< class Head, class... Tail >
struct tuple_element<0, std::tuple<Head, Tail...>> {
typedef Head type;
};
Run Code Online (Sandbox Code Playgroud)
但是,如果元组有很多参数(超过100或200个参数),这个实现需要深度递归实例化.
Q1:为什么C++ 11没有添加特殊运算符来获取索引元素?像元组[2]或元组[0]?
Q2:有可能减少深度实例化吗?例如,在D语言中,更多模板算法(在typetuple中)需要O(log(N))深度实例化.
编辑:Q1:为什么C++ 11没有添加特殊运算符来从变量模板获取索引元素?like template <class ... T> struct index {typedef T [3] third_element;}
我认为这个实现有O(log(N))实例化深度; 荣誉给XEO为O(日志(N))指数特技(修改为使用std::size_t的代替unsigned).
编辑:我意识到有一个不同的,更简单的,可能更快(编译时)的解决方案来获得第n类型的元组.
// from https://stackoverflow.com/a/13073076
// indices trick in O(log(N)) instantiations, by Xeo
// using aliases for cleaner syntax
template<class T> using Invoke = typename T::type;
template<std::size_t...> struct seq{ using type = seq; };
template<class S1, class S2> struct concat;
template<std::size_t... I1, std::size_t... I2>
struct concat<seq<I1...>, seq<I2...>>
: seq<I1..., (sizeof...(I1)+I2)...>{};
template<class S1, class S2>
using Concat = Invoke<concat<S1, S2>>;
template<std::size_t N> struct gen_seq;
template<std::size_t N> using GenSeq = Invoke<gen_seq<N>>;
template<std::size_t N>
struct gen_seq : Concat<GenSeq<N/2>, GenSeq<N - N/2>>{};
template<> struct gen_seq<0> : seq<>{};
template<> struct gen_seq<1> : seq<0>{};
Run Code Online (Sandbox Code Playgroud)
执行/类似于std::tuple_element:
namespace detail
{
template<std::size_t>
struct Any
{
Any(...) {}
};
template<typename T>
struct wrapper { using type = T; };
template<std::size_t... Is>
struct get_nth_helper
{
template<typename T>
static auto deduce(Any<Is>..., wrapper<T>, ...) -> wrapper<T>;
};
template<std::size_t... Is, typename... Ts>
auto deduce_seq(seq<Is...>, wrapper<Ts>... pp)
-> decltype( get_nth_helper<Is...>::deduce(pp...) );
}
#include <tuple>
template<std::size_t n, class Tuple>
struct tuple_element;
template<std::size_t n, class... Ts>
struct tuple_element<n, std::tuple<Ts...>>
{
using wrapped_type = decltype( detail::deduce_seq(gen_seq<n>{},
detail::wrapper<Ts>()...) );
using type = typename wrapped_type::type;
};
Run Code Online (Sandbox Code Playgroud)
用法示例:
#include <typeinfo>
#include <iostream>
int main()
{
std::tuple<int, double, bool, char> t;
tuple_element<1, decltype(t)>::type x;
std::cout << typeid(x).name() << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
感谢@Barry在函数/数组类型的早期版本中指出了一个问题,并提供了修复.
原始版本:(注意:此版本已简化,不添加cv限定符.)
#include <tuple>
namespace detail
{
template < std::size_t Index, class Arg >
struct s_get_one
{
// declare a function that links an Index with an Arg type
friend Arg get(s_get_one, std::integral_constant<std::size_t, Index>);
};
template < typename... Bases >
struct s_get : Bases... {};
}
template < std::size_t I, class T >
struct tuple_element;
template < std::size_t I, class... Args >
struct tuple_element < I, std::tuple<Args...> >
{
template<class T>
struct wrapper { using type = T; };
// deduce indices from seq helper
template < std::size_t... Is >
static auto helper(seq<Is...>)
-> detail::s_get< detail::s_get_one<Is, wrapper<Args>>... >;
// generate indices in O(log(N)) and use name lookup to find the type
using IC = std::integral_constant<std::size_t, I>;
using wrapped_type = decltype( get(helper(gen_seq<sizeof...(Args)>{}), IC{}) );
using type = typename wrapped_type::type;
};
Run Code Online (Sandbox Code Playgroud)
为什么C++ 11没有添加特殊运算符来获取索引元素?像元组2或元组[0]?
首先,因为即使他们这样做了,它仍然以相同的方式工作:递归.元组主要是库特征.虽然他们背负了可变参数模板等语言功能,但它们或多或少在C++ 98/03中具有功能.
其次,这是不可能的.并非没有非常困难的语言变化.
目前尚不清楚你的意思tuple[2].
如果你的意思是std::tuple<int, float, std::string>[2]应该以某种方式解析为typename std::string,那么这意味着你现在需要解释为什么这样做.同样,元组是库特征,而不是语言构造.所以必须有一些语言结构,这typename[integer]是一个有效的结构.那将是什么,它意味着什么?
如果你的意思是:
std::tuple<int, float, std::string> tpl{...};
Run Code Online (Sandbox Code Playgroud)
我们应该能够获得字符串tpl[2],这是几个 "不会发生"的阴影.C++是一种静态类型语言.唯一的原因std::get是能够摆脱它所做的是整数索引不是函数参数; 它是一个模板参数.这就是允许std::get<0>从中返回完全不同类型的内容std::get<2>.这不可能发生operator[](int); 该函数必须始终返回相同的类型.
所以现在你需要有类似的东西template<class T, int i> ... operator[]().这将是非常令人困惑的,因为您不能再tpl[runtimeValue]对该类型执行(因为模板参数必须是编译时值).没有这样的类型operator[]限制在能够处理运行时值.所以你要创造一个非常古怪的类型.
即便如此......它仍然需要进行递归才能获得价值.
有可能减少深度实例化吗?
在编译时间之外(这不是一个不合理的问题),这有什么关系?一个体面的内联将把大部分内容扔掉.
至于编译时,有各种功能的非递归实现std::tuple.他们是否可以tuple_element非递归地做,我不这么认为.这个libc ++实现似乎表明它不能,尽管tuple非递归地实现它本身.
| 归档时间: |
|
| 查看次数: |
1240 次 |
| 最近记录: |