用户定义与自动模板扣除指导优先级

0xd*_*00d 6 c++ templates template-argument-deduction c++17

假设我们有一个这样的类,带有用户定义的演绎指南:

template<typename T, typename... Args>
struct Foo
{
    Foo(Args&&...) { std::cout << "just Args: " << __PRETTY_FUNCTION__ << std::endl; }
    Foo(Args&&..., T&&) { std::cout << "Args and T: " << __PRETTY_FUNCTION__ << std::endl; }
};

template<typename... Args>
Foo(Args&&...) -> Foo<Void, Args...>;
Run Code Online (Sandbox Code Playgroud)

现在让我们尝试创建这个类的实例:Foo foo { 10 };.什么是推导出的模板参数以及将调用哪个构造函数?

经过一些实验后,它取决于编译器.即,GCC 7和铛6(从主干)似乎选择自动引导,实例化TintArgs用空包,因此输出是

Args and T: Foo<T, Args>::Foo(Args&& ..., T&&) [with T = int; Args = {}]
Run Code Online (Sandbox Code Playgroud)

另一方面,clang 5选择用户定义的指南:

just Args: Foo<Void, int>::Foo(Args &&...) [T = Void, Args = <int>]
Run Code Online (Sandbox Code Playgroud)

哪种选择是正确的,在这种情况下如何使用用户定义的演绎指南?

wandbox上提供的完整示例.

Bar*_*rry 3

让我们从首要原则出发。尝试推断Foo{10}涉及对此集进行重载解析:

template <typename T, typename... Args>
Foo<T, Args...> __f(Args&&... ); // ctor #1

template <typename T, typename... Args>
Foo<T, Args...> __f(Args&&..., T&&); // ctor #2

template <typename... Args>
Foo<Void, Args...> __f(Args&&... ); // deduction guide
Run Code Online (Sandbox Code Playgroud)

在第一个构造函数合成的函数中,T是一个非推导的上下文。在从第二个构造函数合成的函数中,Args是一个非推导的上下文。所以两者都不可行。演绎指南是可行的,因此它无疑是最佳可行的候选者,因此我们最终得到了Foo<Void, int>

一旦到达那里,我们再次执行重载解析以选择构造函数。这更简单,第一个是可行的,第二个不是,所以应该调用它。

任何其他行为都是编译器错误(归档号83447)。