I'm trying to define a sort of template 'map' primitive (as in map-reduce). The idea is that I want to apply a function to every item of a template parameter pack. The function can be any callable object. It can return any type (though the return type will be ignored), and it can take additional arguments on top of the item in question.
The tricky part is that I effectively have two parameter packs that I need to deal with. They'll initially be packed together, but I want to split them using template specialization. What follows is my attempt at doing this.
In case it isn't obvious (due to the auto keyword in the template parameter list), this is using 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)
{
}
The idea is to have a wrapper that looks something like
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) ...);
}
Which I would then use as
map <foo> (args_for_foo..., items_to_map_over...);
Unfortunately, when I try to compile this (using clang++), I get the following error.
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.
Now, I wouldn't be surprised if it doesn't like the fact that ArgumentTypes...
shows up twice in my specializations, though it isn't saying so directly.
What exactly is going wrong, and how might I build my map primitive in a way that avoid this? I don't want to store copies or references to any of the arguments, because that shouldn't be necessary. If I were manually writing a template that specialized the function and parameter types, I wouldn't need to store anything. This rules out tuple wrappers as an option.
EDIT: Added usage information, as requested.
EDIT: Fixed overzealous use of rvalue reference qualifiers, to avoid confusion.