首页 文章

使用模板特化分割可变参数包

提问于
浏览
0

我正在尝试定义一种模板'map'原语(如map-reduce) . 我的想法是我想将一个函数应用于模板参数包的每个项目 . 该函数可以是任何可调用对象 . 它可以返回任何类型(虽然返回类型将被忽略),并且它可以在有问题的项目之上采取其他参数 .

棘手的部分是我有效地拥有两个需要处理的参数包 . 它们最初会被打包在一起,但我想使用模板特化来拆分它们 . 以下是我尝试这样做的尝试 .

如果不明显(由于模板参数列表中的auto关键字),这是使用C 17 .

#include <utility>

template <class Signature, auto f, class... ArgsAndItems>
struct Map;

template
<
    class ReturnType,
    class Item,
    class... ArgumentTypes,
    auto f,
    class... Items
>
struct Map
<
    ReturnType (Item, ArgumentTypes...),
    f,
    ArgumentTypes...,
    Item,
    Items...
>
{
    static void
    function (ArgumentTypes &&... arguments, Item && item, Items &&... items);
};

template <class ReturnType, class Item, class... ArgumentTypes, auto f>
struct Map<ReturnType (Item, ArgumentTypes...), f, ArgumentTypes...>
{
    static void
    function (ArgumentTypes &&... arguments);
};

template
<
    class ReturnType,
    class Item,
    class... ArgumentTypes,
    auto f,
    class... Items
>
void
Map
<
    ReturnType (Item, ArgumentTypes...),
    f,
    ArgumentTypes...,
    Item,
    Items...
>::function (ArgumentTypes &&... arguments, Item && item, Items &&... items)
{
    f (std::forward<Item> (item), std::forward<ArgumentTypes> (arguments)...);
    Map
    <
        ReturnType (Item, ArgumentTypes ...),
        f,
        ArgumentTypes...,
        Items...
    >::function
    (
        std::forward<ArgumentTypes> (arguments)...,
        std::forward<Items> (items)...
    );
}

template <class ReturnType, class Item, class... ArgumentTypes, auto f>
void
Map
<
    ReturnType (Item, ArgumentTypes...),
    f,
    ArgumentTypes...
>::function (ArgumentTypes &&... arguments)
{
}

我们的想法是拥有一个类似的包装器

template <auto f, class ... ArgsAndItems>
void
map (ArgsAndItems && ... args_and_items)
{
    Map
    <
        decltype (decltype (f)::operator ()),
        f,
        ArgsAndItems...
    >::function (std::forward <ArgsAndItems> (args_and_items) ...);
}

然后我会用它作为

map <foo> (args_for_foo..., items_to_map_over...);

不幸的是,当我尝试编译它(使用clang)时,我收到以下错误 .

map.hpp:14:8: error: class template partial specialization contains template
      parameters that cannot be deduced; this partial specialization will never
      be used
      [-Wunusable-partial-specialization]
struct Map
       ^~~
map.hpp:8:8: note: non-deducible template parameter 'ReturnType'
        class ReturnType,
              ^
map.hpp:9:8: note: non-deducible template parameter 'Item'
        class Item,
              ^
map.hpp:10:11: note: non-deducible template parameter 'ArgumentTypes'
        class... ArgumentTypes,
                 ^
map.hpp:11:7: note: non-deducible template parameter 'f'
        auto f,
             ^
map.hpp:12:11: note: non-deducible template parameter 'Items'
        class... Items
                 ^
1 error generated.

现在,我不会喜欢 ArgumentTypes... 在我的专业化中出现两次这一事实,尽管它没有直接说出来 .

What exactly is going wrong, and how might I build my map primitive in a way that avoid this? 我不需要 . 如果我手动编写专门用于函数和参数类型的模板,我不需要存储任何东西 . 这排除了元组包装器作为选项 .

编辑:根据要求添加使用信息 .

编辑:修复过度使用右值参考限定符,以避免混淆 .

1 回答

  • 1

    到底出了什么问题,[...]

    有几个问题因此可能更容易从一个有效的简单基本示例开始(没有完美的转发) .

    [...]以及如何以避免这种情况的方式构建我的 Map 基元?

    您可以分离参数包:

    • 参数类型作为 Map 的模板参数传递

    • 项类型作为 Map::function 的模板参数传递

    这是一个没有完美转发的工作示例 . 由于可能的cv限定符和ref限定符,因此推论参数类型并不完整 .

    #include <iostream>
    
    template<class F, class... Args>
    struct Map {
      template<class... Items>
      static void function(Args... args, Items... items) {
        static constexpr auto f = F{};
    
        // here comes a fold expression
        // see https://en.cppreference.com/w/cpp/language/fold
    
        ( f(items, args...), ...); // "fold over comma operator"
      }
    };
    
    ////////////////////////////////////////////////////////////////////////////////
    
    template<class F, class Ret, class Item, class... Args, class... ArgsItems>
    void map_impl(Ret(F::*)(Item, Args...) const, ArgsItems... args_items) {
      Map<F, Args...>::function(args_items...);
    }
    
    template<class F, class... ArgsItems>
    void map(ArgsItems... args_items) {
      map_impl<F>(&F::operator(), args_items...);
    }
    
    ////////////////////////////////////////////////////////////////////////////////
    
    struct print_x_m_plus_n {
      void operator()(int x, int m, int n) const {
        int y = x * m + n;
        std::cout << y << std::endl;
      }
    };
    
    int main() {
      constexpr int m = 2;
      constexpr int n = 1;
    
      map<print_x_m_plus_n>(m, n, 0, 1, 2);
    }
    

    输出:

    1
    3
    5
    

相关问题