我想写一个函数来遍历 std::tuple<...>
.迭代本身不会产生有关元组模板类型的任何问题,因为“...”具有相同的类型(如 int、int、int、...)。我已经使用模板元编程实现了一个带有辅助结构“Helper”的工作函数“Foo”——一切都很好。
但是当我想实现一个替代版本时,使用 constexpr 函数“helper”,编译器 (g++ 5.2.0) 陷入错误消息的无限循环。根据我从这些消息中得到的信息,“位置”模板参数被实例化为大得离谱 (== 4294967245) 而不是 (== 1)。我试图让两个版本在语法和术语上尽可能接近。
最小示例
#include <tuple>
// template metaprogramming version
template
<class T, std::size_t position>
struct Helper{
static int
help(T tuple) {
return std::get<position>(tuple) +
Helper<T,position - 1>::help(tuple);
}
};
// template metaprogramming version, specialized
template
<class T>
struct Helper<T,0>{
static int
help(T tuple) {
return std::get<0>(tuple);
}
};
// function version, not working
template
<class T, std::size_t position>
constexpr int
helper(T tuple) {
return
0 == position ?
std::get<position>(tuple) + helper<T,position-1>(tuple) :
std::get<0>(tuple);
}
template
<class T>
auto
Foo(T tuple) {
constexpr std::size_t dimension = std::tuple_size<T>::value;
// working version, using the helper struct
return Helper<T,dimension - 1>::help(tuple);
// wrong(?) version, using the constexpr helper function
return helper<T,dimension - 1>(tuple);
}
int main() {
std::tuple<int,int> t(1,1);
Foo(t);
return 0;
}
我的问题:
- 在编译期间尝试这种迭代主要是错误的吗 使用 constexpr 函数的时间?
- 如果不是,是不是编译器的bug 或者正确的版本应该是什么样的?
我完全意识到,由于元组 (int,int,...) 中的类型相同,因此可以使用 vector 实现类似的版本。但我认为元组版本在概念上更适合我的问题,并且在运行时速度更快。
最佳答案
当你有一个函数模板时——所有的代码必须被编译出来。所以在这里:
template
<class T, std::size_t position>
constexpr int helper(T tuple) {
return
0 == position ?
std::get<position>(tuple) + helper<T,position-1>(tuple) :
std::get<0>(tuple);
}
我们总是编译条件的两个部分(旁注:您的条件是向后的)。所以当position == 0
, std::get<0>(tuple)
部分编译和 std::get<0>(tuple) + helper<T, -1>(tuple)
部分编译。但是要做到这一点,我们需要编译 helper<T, -2>(tuple)
.和 helper<T, -3>(tuple)
.我们无限递归。
您的特化方法之所以有效,是因为 Helper<T, 0>
就是std::get<0>
.那里没有其他逻辑,所以我们停止。如果您想在功能上执行此操作,更简单的方法是将位置作为参数传递。即:
template <std::size_t position>
using Pos = std::integral_constant<std::size_t, position>; // to save me some typing
template <typename T>
constexpr int helper(T const& tuple, Pos<0> )
{
return std::get<0>(tuple);
}
template <typename T, std::size_t position>
constexpr int helper(T const& tuple, Pos<position> )
{
return std::get<position>(tuple) + helper(tuple, Pos<position - 1>{});
}
在这里,我们通过重载执行条件 - 所以一旦我们到达 helper(T, Pos<0> )
,我们成功地终止了递归。
在 C++1z 中,折叠表达式使这变得容易得多,您可以在其中执行以下操作:
template <typename T>
constexpr int sum_tuple(T const& tuple) {
return sum_tuple(tuple, std::make_index_sequence<std::tuple_size<T>::value>{});
}
template <typename T, std::size_t... Is>
constexpr int sum_tuple(T const& tuple, std::index_sequence<Is...> )
{
return (std::get<Is>(tuple) + ... );
}
关于c++ - 使用 constexpr 的替代元组迭代,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32633089/