77

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

具体来说,第一个说它是这样的,然后重定向到“详细信息”的标准,而第二个引用标准,至少可以说是神秘的。

有人可以向像我这样的凡人解释什么是非推断上下文,它何时发生以及为什么会发生?

4

1 回答 1

113

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

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

现在,如果您说f(x),您在哪里声明std::vector<int> x;,则T推断int并且您获得了专业化f<int>

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

但是,也有演绎的上下文,其中不可能进行演绎。典型示例是“出现在 a 左侧的模板参数::

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>to的转换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)。)

于 2014-08-11T14:26:36.230 回答