我有一个函数,其中模板类型参数跟随参数包。它看起来像这样:
template<typename...Args, typename T>
T* default_factory_func()
{
return new T;
}
Visual c++编译器拒绝它,并报错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'
.
然而,我尝试了编译器资源管理器上可用的各种版本的GCC(从4.4.7开始)和clang(从3.1开始),它们都可以很好地编译这些代码。
// 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>
);
}
这是MSVC的一些怪癖还是标准不允许的?
我认为这里的标准是混乱的(可能需要一个问题,如果没有的话)。
default_factory_func
的定义是不正确的per [temp.param]
函数模板的模板形参包后面不能跟另一个模板形参,除非该模板形参可以从函数模板的形参类型列表([dcl.fct])中推导出来,或者有默认实参
- 同时,
default_factory_func
的类型可以(有争议地)根据[temp. deduction .funcaddr]推断,因为当您通过&default_factory_func<int,int,int>
时,您正在尝试匹配
some_class*(*)(void)
的目标类型。模板实参可以从接受重载集地址时指定的类型推导出来。如果存在目标,则函数模板的函数类型和目标类型被用作P和a的类型,并按照[temp. deduction .type]中的描述进行演绎。否则,使用类型P和A
的空集执行演绎。
(感谢n.m.在他们现已删除的答案中指出了第二个问题)
我认为最安全的办法是通过重新排列模板参数来避免与第一条规则发生冲突:
template<class T, typename...Args>
T* default_factory_func()
{
return new T(Args()...);
}
然后显式地转换函数指针来解析重载:
auto foo = construct(
static_cast<some_class*(*)()>(default_factory_func<some_class, int, int, int>)
);
<<h3>生活代码/h3>(在gcc/clang/和msvc latest上编译)