c++ - 使用 constexpr 的替代元组迭代

标签 c++ templates c++11 c++14

我想写一个函数来遍历 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/

相关文章:

c++ - GLib - C++ 中的主事件循环

c++ - 在不触及主模板的情况下使用 SFINAE 部分特化

c++ - 在类之间共享成员函数

c++ - 如何通过指针从函数访问类的成员?

c++ - 尝试将字符串推到列表后面时出现段错误

c++ - 两个 vector 的可迭代并集

c++ - 分配 char 缓冲区以保存 float 的文本表示

c++ - 为什么不能将对继承自(非模板)类 B 的类模板 A 的引用转换为对 B 的引用?

c++ - 模板类中具有依赖类型参数的模板函数

c++ - std::unordered_set::erase 复杂度