Разделить пакет с переменными параметрами, используя специализацию шаблона - PullRequest
0 голосов
/ 22 ноября 2018

Я пытаюсь определить своего рода шаблонный примитив '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... дважды появляется в моей специализации, хотя это не говорит об этом напрямую..

Что именно идет не так, и как я могу построить примитив карты таким образом, чтобы этого избежать? Я не хочу хранить копии или ссылки на какие-либо аргументы,потому что это не должно быть необходимым.Если бы я вручную писал шаблон, специализирующий типы функций и параметров, мне не нужно было бы ничего хранить.Это исключает обертки для кортежей в качестве опции.

РЕДАКТИРОВАТЬ: по запросу добавлена ​​информация об использовании.

РЕДАКТИРОВАТЬ: Исправлено чрезмерное использование ссылочных квалификаторов rvalue во избежание путаницы.

1 Ответ

0 голосов
/ 23 ноября 2018

Что именно идет не так, [...]

Существует несколько проблем, поэтому может быть проще начать с простого базового примера, который работает (без идеальной пересылки).

[...] и как мне построить примитив карты таким образом, чтобы этого избежать?

Вы можете разделить пакеты параметров:

  • типы аргументов передаются в качестве аргументов шаблона для 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
...