用户定义与自动模板推导指导优先级
User-defined vs automatic template deduction guides priorities
假设我们有一个像这样的 class 和用户定义的推导指南:
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...>;
现在让我们尝试创建此 class 的实例:Foo foo { 10 };
。推导的模板参数是什么以及将调用什么构造函数?
经过一些实验证明这取决于编译器。也就是说,gcc 7 和 clang 6(来自 trunk)似乎选择了自动引导,用 int
实例化 T
,用空包实例化 Args
,因此输出是
Args and T: Foo<T, Args>::Foo(Args&& ..., T&&) [with T = int; Args = {}]
clang 5,另一方面,选择用户自定义指南:
just Args: Foo<Void, int>::Foo(Args &&...) [T = Void, Args = <int>]
哪种选择是正确的,在这种情况下如何使用自定义演绎指南?
上提供完整示例
让我们从第一原则开始。尝试从 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
在第一个构造函数合成的函数中,T
是一个非推导上下文。在从第二个构造函数合成的函数中,Args
是一个非推导上下文。所以两者都不可行。演绎指南是可行的,所以它是最可行的候选者,所以我们最终得到 Foo<Void, int>
.
到达那里后,我们再次执行重载决策以选择构造函数。这个比较直接,第一个可行,第二个不可行,所以应该调用。
任何其他行为都是编译器错误(已提交 83447)。
假设我们有一个像这样的 class 和用户定义的推导指南:
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...>;
现在让我们尝试创建此 class 的实例:Foo foo { 10 };
。推导的模板参数是什么以及将调用什么构造函数?
经过一些实验证明这取决于编译器。也就是说,gcc 7 和 clang 6(来自 trunk)似乎选择了自动引导,用 int
实例化 T
,用空包实例化 Args
,因此输出是
Args and T: Foo<T, Args>::Foo(Args&& ..., T&&) [with T = int; Args = {}]
clang 5,另一方面,选择用户自定义指南:
just Args: Foo<Void, int>::Foo(Args &&...) [T = Void, Args = <int>]
哪种选择是正确的,在这种情况下如何使用自定义演绎指南?
上提供完整示例让我们从第一原则开始。尝试从 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
在第一个构造函数合成的函数中,T
是一个非推导上下文。在从第二个构造函数合成的函数中,Args
是一个非推导上下文。所以两者都不可行。演绎指南是可行的,所以它是最可行的候选者,所以我们最终得到 Foo<Void, int>
.
到达那里后,我们再次执行重载决策以选择构造函数。这个比较直接,第一个可行,第二个不可行,所以应该调用。
任何其他行为都是编译器错误(已提交 83447)。