繁体   English   中英

使用模板专门化拆分可变参数包

[英]Split variadic parameter pack using template specialization

我正在尝试定义一种模板“地图”原语(如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...在我的专业领域中出现两次的事实,我不会感到惊讶,尽管它并没有这么直接说。

到底出了什么问题?如何避免这种情况来构建地图基元? 我不想存储任何参数的副本或引用,因为这不是必需的。 如果我是手动编写专门用于函数和参数类型的模板,则无需存储任何内容。 这排除了元组包装器作为选项。

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

编辑:固定过度使用右值引用限定符,以避免混淆。

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

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

[...]以及如何避免这种情况构建地图基元?

您可以分离参数包:

  • 参数类型作为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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM