函数模板中的参数包后面是否可以跟另一个取决于返回类型的参数?

Hed*_*ede 6 c++ visual-c++ variadic-templates

我有一个函数,其中模板类型参数跟随参数包。它看起来像这样:

template<typename...Args, typename T>
T* default_factory_func()
{
    return new T;
}
Run Code Online (Sandbox Code Playgroud)

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>
    );
}

Run Code Online (Sandbox Code Playgroud)

这是 MSVC 的一些怪癖还是标准不允许的?

And*_*dyG 1

我认为这里的标准很混乱(如果标准尚不存在,则可能需要一个问题)。

函数模板的模板参数包后面不应跟有另一个模板参数,除非该模板参数可以从函数模板的参数类型列表([dcl.fct])推导出来或具有默认参数

  • 同时,default_factory_func可以(可以说)根据[temp.deduct.funcaddr]some_class*(*)(void)推断出 的类型,因为当您传递时,您试图匹配 的目标类型&default_factory_func<int,int,int>

模板参数可以从获取重载集的地址时指定的类型推导出来。如果有目标,则将函数模板的函数类型和目标类型作为P和A的类型,并按照[temp.deduct.type]中的描述进行推导。否则,用类型 P 和 A 的空集进行推导

(感谢nm在其现已删除的答案中指出了第二个)

我认为最安全的选择是通过重新排序模板参数来避免违反第一条规则:

template<class T, typename...Args>
T* default_factory_func()
{
    return new T(Args()...);
}
Run Code Online (Sandbox Code Playgroud)

然后显式转换函数指针来解决重载:

auto foo = construct(
    static_cast<some_class*(*)()>(default_factory_func<some_class, int, int, int>)
);
Run Code Online (Sandbox Code Playgroud)

实时代码

(最新的 gcc/clang/ 和 msvc 上编译)