0

我写了一个可变参数模板,它正在执行一个函子F 正好N次并累计结果。现在我想知道如何使这个模板能够处理目前名为id和所需功能x, y, z的可变数量的索引(模板)参数。递归模板函数遍历几个模板参数

我想到的是一个像下面这样的函子,它被执行,就好像你称它为三个循环一样。我也怀疑它是否可以通过参数列表来解决。

struct 3DFunctor { 
    template <int x, int y, int z> 
    static int run() { 
     return x*y*z; 
    } 
}; 

期望的行为应该是这样的:

accum_for<3,3,3>::get<3DFunctor>(); 

3DFunctor::run<0,0,0>(/*args ...*/) + 
3DFunctor::run<0,0,1>(/*args ...*/) + 
3DFunctor::run<0,0,2>(/*args ...*/) + 
3DFunctor::run<0,1,0>(/*args ...*/) + 
3DFunctor::run<0,1,1>(/*args ...*/) + 
3DFunctor::run<0,1,2>(/*args ...*/) + 
// .. 

例子:

#include <iostream> 
#include <string> 

struct F { 
    template <int id> 
    static int run(int val) { 
     return id * val; 
    } 
}; 

template<unsigned int n> 
struct accum1d_for { 
    template <class Funct, class ... ArgTypes> 
    static auto get(ArgTypes ... args) -> decltype(Funct::template run<0>(args ...)) { 
     return (
      accum1d_for<n-1>::template get<Funct>(args ...) + 
      Funct::template run<n>(args ...) 
     ); 
    } 
}; 

template<> 
struct accum1d_for<0> { 
    template <class Funct, class ... ArgTypes> 
    static auto get(ArgTypes ... args) -> decltype(Funct::template run<0>(args ...)) { 
     return static_cast<decltype(Funct::template run<0>(args ...))>(0); 
    } 
}; 

int main() 
{ 
    std::cout << accum1d_for<10>::get<F>(2.f) << std::endl; 
} 
+1

加期望的行为 – dgrat

+0

尝试了“使用blah =”但得到了编译器错误。不确定这是否适用于函数体。 – dgrat

+1

极少的语言版本? C++ 11,C++ 14或C++ 17? – max66

回答

1

我建议一个C++ 14解决方案(使用std::index_sequencestd::make_index_sequence();如果您需要一个C++ 11解决方案应该是简单的创建一个替代品)。

我已将constexpr添加到您的get(),因此您可以使用它初始化constexpr值。

而且我用std::size_t代替int,因为你写了关于“索引”的内容,所以它更简单一些。

示例

#include <utility> 
#include <iostream> 

struct Functor3D 
{ 
    template <std::size_t x, std::size_t y, std::size_t z> 
    static constexpr std::size_t run() 
    { return x*y*z; } 
}; 

template <std::size_t ... topIs> 
struct accum_for 
{ 
    private: 
     template <std::size_t ... Is> 
     using IndS = std::index_sequence<Is...>; 

     template <typename Func, std::size_t ... Is> 
     static constexpr std::size_t h1 (IndS<Is...> const &) 
     { return Func::template run<Is...>(); } 

     template <typename Func, std::size_t ... I0s, 
       std::size_t ... Ins, typename ... Ts> 
     static constexpr std::size_t h1 (IndS<I0s...>, IndS<Ins...>, 
             Ts const & ... ts) 
     { 
     using unused = int[]; 

     std::size_t ret { 0 }; 

     (void)unused { 0, 
      ((void)(ret += h1<Func>(IndS<I0s..., Ins>{}, ts...)), 0)... }; 

     return ret; 
     } 

    public: 
     template <typename Func> 
     static constexpr std::size_t get() 
     { return h1<Func>(IndS<>{}, std::make_index_sequence<topIs>()...); } 
}; 

int main(void) 
{ 
    constexpr std::size_t val { accum_for<3U, 3U, 3U>::get<Functor3D>() }; 

    std::cout << val << std::endl; 
} 

如果可以使用C++ 17,h1()辅助功能(一个与unused阵列)的通用版本可以简化如下

template <typename Func, std::size_t ... I0s, 
      std::size_t ... Ins, typename ... Ts> 
    static constexpr std::size_t h1 (IndS<I0s...>, IndS<Ins...>, 
            Ts const & ... ts) 
    { return (h1<Func>(IndS<I0s..., Ins>{}, ts...) + ...); } 
+0

它看起来工作但看起来很复杂。你能帮我理解一些表情吗? a)使用unused = int [];这是数组的别名吗? b)为什么数组初始值设定项{...}缺少一个=运算符? c)为什么必须进行无效转换? – dgrat

+0

@dgrat - 它看起来很复杂,但是你问的并不简单(但C++ 17版本更简单); (a)是:它是数组的别名;它被使用是因为你不能写'int [] {}'(不给变量命名),但你可以写'unused {}'; (b)它被称为“统一初始化”,从C++ 11开始引入; (c)有两个“空白转换”;第一个('(void)未使用')不是必需的,但是可以避免类型为“未使用的值”(或类似的)的警告。第二个('(void)(ret + = ...')有必要避免某些版本的clang ++中的错误 – max66

3

随着std::index_sequence,你可以做

template <std::size_t N1, std::size_t N2, std::size_t N3> 
struct accum_for 
{ 
private: 
    template <class Funct, std::size_t ... Is> 
    static int get(std::index_sequence<Is...>) { 
     int res = 0; 
     using int_array = int[]; 
     static_cast<void>(int_array{0, (res += Funct::template run<Is/N3/N2, 
                    (Is/N3) % N2, 
                    Is % N3>())...}); 
     return res; 
    } 


public: 
    template <class Funct> 
    static int get() { 
     return get<Funct>(std::make_index_sequence<N1 * N2 * N3>()); 
    } 
}; 

Demo

+0

3个索引(+1)的精彩解决方案,但OP要求解决方案“模板能够处理可变数量的索引(模板)参数” – max66

1

给定n个元组,生成它们的叉积(每个元素中包含每个元素的元组的元组)。

给定一个整型常量的元组,使用它们的值调用运行。

给定一个值,生成一个从0到n-1的整型常量的元组。

给定一个元组,在每个元素上执行一个函数对象。

3和1定义要运行的立方体。

传递立方体4,用的λ,做2.

虽然woukd是有趣写,不值得一起更换9行代码。以上所有操作都不是以std预先编写的;我假设大多数元编程库会实现它们。对于上述每个步骤,使用std(可能少得多)从头开始编写将少于50行。