使用constexpr替代元组迭代

cla*_*dio 3 c++ templates c++11 c++14

我想编写一个迭代的函数std::tuple<...>.迭代本身不会产生关于元组模板类型的任何问题,因为'...'具有相同的类型(如int,int,int,...).我使用模板metapgrogramming实现了一个带有辅助结构'Helper'的工作函数'Foo' - 一切都很好.

但是当我想使用constexpr函数'helper'来实现替代版本时,编译器(g ++ 5.2.0)会陷入无限循环的错误消息中.根据我从这些消息中得到的结果,'position'模板参数被实例化为可笑的大(== 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;
}
Run Code Online (Sandbox Code Playgroud)

我的问题:

  • 在编译时使用constexpr函数尝试这种迭代主要是错误的吗?
  • 如果没有,它是编译器中的错误或正确的版本应该如何?

我完全清楚,由于元组中的相同类型(int,int,...),可以使用向量实现类似的版本.但我认为元组版本在概念上对我的问题更好,在运行时更快.

Bar*_*rry 6

当你有一个功能模板 - 所有代码都必须编译出来.所以在这里:

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);
}
Run Code Online (Sandbox Code Playgroud)

我们总是编译条件的两个部分(侧注:你的条件是向后).所以,当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>{});
}
Run Code Online (Sandbox Code Playgroud)

在这里,我们通过重载执行条件 - 所以一旦我们到达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) + ... );
}
Run Code Online (Sandbox Code Playgroud)