什么是非演绎的语境?

What is a nondeduced context?

本文关键字:语境 演绎 是非 什么      更新时间:2023-10-16

我绊倒了"为什么模板参数演绎不工作在这里?,答案可以总结为"这是一个非推断的语境"。

具体来说,第一个说它是这样的事情,然后重定向到标准的"细节",而第二个引用标准,这至少可以说是神秘的。

谁能给我这样的凡人解释一下,什么是非演绎上下文,它什么时候发生,为什么发生?

演绎是指根据给定实参确定模板形参类型的过程。它适用于函数模板、auto和其他一些情况(例如部分专门化)。例如,考虑:

template <typename T> void f(std::vector<T>);

现在如果你说f(x),你声明的std::vector<int> x;,那么T推导出int,你得到专门化f<int>

为了进行演绎,要演绎的模板形参类型必须出现在可演绎的上下文中。在本例中,f的函数参数就是这样一个可演绎的上下文。也就是说,函数调用表达式中的实参允许我们确定模板形参T应该是什么才能使调用表达式有效。

然而,也有演绎的上下文,其中不可能演绎。典型的例子是"出现在::的左侧的模板参数:
template <typename> struct Foo;
template <typename T> void g(typename Foo<T>::type);

在此函数模板中,函数形参列表中的T位于非推导的上下文中。因此,你不能说g(x)并推断出T。其原因是不存在"反向对应"。成员 Foo<T>::type。例如,您可以使用专门化:

 template <> struct Foo<int>       { using type = double; };
 template <> struct Foo<char>      { using type = double; };
 template <> struct Foo<float>     { using type = bool; };
 template <> struct Foo<long>      { int type = 10; };
 template <> struct Foo<unsigned>  { };

如果您呼叫g(double{}), T有两个可能的应答,如果您呼叫g(int{})则没有应答。一般来说,类模板形参和类成员之间没有关系,因此不能执行任何合理的实参演绎。


有时明确地禁止论证演绎是有用的。这是std::forward的例子。另一个例子是当您从Foo<U>转换到Foo<T>,或者其他转换(例如std::stringchar const *)时。现在假设你有一个自由函数:

template <typename T> bool binary_function(Foo<T> lhs, Foo<T> rhs);

如果调用binary_function(t, u),则演绎可能是二义性的,因此失败。但是可以合理地只推导一个实参,而不能推导另一个实参,从而允许隐式转换。现在需要一个显式的非演绎上下文,例如:

template <typename T>
struct type_identity {
    using type = T;
};
template <typename T>
bool binary_function(Foo<T> lhs, typename type_identity<Foo<T>>::type rhs)
{
    return binary_function(lhs, rhs);
}

(您可能在使用std::min(1U, 2L)时遇到过这样的演绎问题)

注意:std::type_identity从c++ 20开始在标准库中可用