I have a function where a template type parameter follows a parameter pack. It looks like this:
template<typename...Args, typename T>
T* default_factory_func()
{
return new T;
}
Visual C++ compiler rejects it with an error C3547: template parameter 'T' cannot be used because it follows a template parameter pack and cannot be deduced from the function parameters of 'default_factory_func'
.
However, I tried various versions of GCC (starting with 4.4.7) and clang (starting with 3.1) available on Compiler Explorer, and all of them compile such code just fine.
// this code is just a minimal example condensed
// from a much more complex codebase
template<typename T>
T* construct(T* (*factory_func)())
{
return factory_func();
}
template<typename...Args, typename T>
T* default_factory_func() // C3547 on this line
{
return new T(Args()...);
}
struct some_class {
some_class(int, int, int) {}
};
int main()
{
construct<some_class>(
default_factory_func<int,int,int>
);
}
Is this some quirk of MSVC or is it not allowed by the standard?
I think the standard is confused here (probably needs an issue if one doesn't already exist).
default_factory_func
is ill-formed per [temp.param]A template parameter pack of a function template shall not be followed by another template parameter unless that template parameter can be deduced from the parameter-type-list ([dcl.fct]) of the function template or has a default argument
default_factory_func
can be (arguably) deduced per [temp.deduct.funcaddr] because you are attempting to match a target type of some_class*(*)(void)
when you pass &default_factory_func<int,int,int>
Template arguments can be deduced from the type specified when taking the address of an overload set. If there is a target, the function template's function type and the target type are used as the types of P and A, and the deduction is done as described in [temp.deduct.type]. Otherwise, deduction is performed with empty sets of types P and A
(Thanks to n.m. for pointing this second one out in their now-deleted-answer)
I think the safest bet would be to avoid running afoul of the first rule by reordering your template arguments:
template<class T, typename...Args>
T* default_factory_func()
{
return new T(Args()...);
}
And then explicitly casting your function pointer to resolve the overload:
auto foo = construct(
static_cast<some_class*(*)()>(default_factory_func<some_class, int, int, int>)
);
(Compiles on gcc/clang/and msvc latest)