I am getting a "wrong number of template arguments (2, should be 1)" error that I can't really understand.
I have a class that provides some helper function for other types that want to interact with it, setting a first template parameter on which they must agree to be compatible with each other automatically on creation. In order to do this in a convenient, general way, I decided to go for a variadic template, that is intended to both be passed constructor arguments and additional template arguments the object type that is to be created takes:
template<typename INTERNAL_TYPE>
class Linker
{
template< template<typename, typename ...> class INPUT_OBJ_TYPE, class ... TEMPLATE_ARGS, class ... CONSTRUCTOR_ARGS >
std::shared_ptr< INPUT_OBJ_TYPE<INTERNAL_TYPE,TEMPLATE_ARGS ...> > getLinked( CONSTRUCTOR_ARGS ... args )
{
std::shared_ptr< INPUT_OBJ_TYPE<INTERNAL_TYPE,TEMPLATE_ARGS ...> > ptr = std::make_shared< INPUT_OBJ_TYPE<INTERNAL_TYPE,TEMPLATE_ARGS ...> >( args... );
return ptr;
}
};
This works for the following class:
template<typename INTERNAL_TYPE, typename SECOND_TYPE>
class TEST_CLASS_1
{};
That is, I can do the following:
Linker<int> container;
auto test_1 = container.getLinked<TEST_CLASS_1,double>();
Then I tried to do the same with another class that is only templated on one argument:
template<typename INTERNAL_TYPE>
class TEST_CLASS_2
{};
auto test_2 = container.getLinked<TEST_CLASS_2>();
But get the above mentioned error.. Why? Playing around with the code I figured as much as that it would compile for the second test (no more for the first one though) if I removed TEMPLATE_ARGS from the function declaration/definition. So I figured that the compiler at that point does not yet realize TEMPLATE_ARGS is empty for the second test and throws an error for having too many template arguments. So I thought I might need to use a trailing return type like
template< template<typename, typename ...> class INPUT_OBJ_TYPE,
class ... TEMPLATE_ARGS, class ... CONSTRUCTOR_ARGS >
auto getLinked( CONSTRUCTOR_ARGS ... args )
-> std::shared_ptr< INPUT_OBJ_TYPE<TREE_TYPE,TEMPLATE_ARGS ...> >
or using decltype
but that didn't work either:
template< template<typename, typename ...> class INPUT_OBJ_TYPE,
class ... TEMPLATE_ARGS, class ... CONSTRUCTOR_ARGS >
auto getLinked( CONSTRUCTOR_ARGS ... args )
->decltype(std::shared_ptr< INPUT_OBJ_TYPE<TREE_TYPE,TEMPLATE_ARGS ...> >())
Am I right about the problem? And how can I solve it?
Thanks a lot!
This might be a compiler bug.
A way to get what you want is to provide two functions, your original and another that doesn't have the non-deducible parameter pack:
template<typename INTERNAL_TYPE>
struct Linker
{
template<
template<typename, typename ...> class INPUT_OBJ_TYPE,
class ... TEMPLATE_ARGS,
class ... CONSTRUCTOR_ARGS
>
std::shared_ptr<
INPUT_OBJ_TYPE<INTERNAL_TYPE,TEMPLATE_ARGS...>
> getLinked( CONSTRUCTOR_ARGS ... args )
{
std::shared_ptr< INPUT_OBJ_TYPE<INTERNAL_TYPE,TEMPLATE_ARGS...> > ptr =
std::make_shared< INPUT_OBJ_TYPE<INTERNAL_TYPE,TEMPLATE_ARGS...> >( args... );
return ptr;
}
template<
template<typename, typename ...> class INPUT_OBJ_TYPE,
class ... CONSTRUCTOR_ARGS
>
std::shared_ptr<
INPUT_OBJ_TYPE<INTERNAL_TYPE>
> getLinked( CONSTRUCTOR_ARGS ... args )
{
std::shared_ptr< INPUT_OBJ_TYPE<INTERNAL_TYPE> > ptr =
std::make_shared< INPUT_OBJ_TYPE<INTERNAL_TYPE> >( args...);
return ptr;
}
};
It appears that the initial substitution of TEST_CLASS_2
is hitting the compiler's "every valid specialization requires an empty pack" code path - that is, the following template is ill-formed, no diagnostic required:
template<class... TArgs, class... CArgs>
std::shared_ptr<TEST_CLASS_2<INTERNAL_TYPE, TArgs...>> getLinked( CArgs... args )
{
/* ... */
}
because every valid specialization of it requires TArgs
to be an empty pack. The initial substitution into getLinked
will indeed result in something similar, but of course, you didn't actually write that template, so I'm not convinced that there should be an error, but whatever.
A possible workaround is to defer substitution of INPUT_OBJ_TYPE
until after deduction, by taking a tag type and making everything deduced:
template< template<class, class...> class, class... > struct tag {};
template< template<class, class...> class INPUT_OBJ_TYPE,
class... TArgs, class... CArgs >
auto getLinked(tag<INPUT_OBJ_TYPE, TArgs...>, CArgs... args )
-> std::shared_ptr<INPUT_OBJ_TYPE<INTERNAL_TYPE,TArgs...>>
{
/* ... */
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With