提问者:小点点

什么是非演绎上下文?


我曾被“为什么模板参数推导在这里不起作用?” 最近,答案可以归结为“这是一个非推导的上下文”。

具体地说,第一个说它是这样的东西,然后重定向到标准以获取“细节”,而第二个引用标准,这至少可以说是隐晦的。

有没有人能向像我这样的凡人解释一下,什么是非演绎的语境,它何时出现,为什么会出现?


共1个答案

匿名用户

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

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

现在,如果您说f(x),在这里您声明了std::vector; x;,则t被推论为int,您将得到专门化f

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

然而,也有非演绎的语境,在那里没有演绎是可能的。 典型的例子是“出现在:::

template <typename> struct Foo;

template <typename T> void g(typename Foo<T>::type);

在此函数模板中,函数参数列表中的T处于非推导上下文中。 因此,不能说出g(x)并推导出t。 这是因为任意类型和成员foo::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就是这种情况。 另一个例子是当您有从foofoo的转换,或者其他转换(想想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)的扣减问题。)