不可用函数模板的类型演绎
Type deduction for non-viable function templates
在他对这个问题的回答和评论部分,Johannes Schaub说,当试图对一个函数模板进行模板类型推断时,需要比传递的参数更多的参数时,会出现"匹配错误":
template<class T>
void foo(T, int);
foo(42); // the template specialization foo<int>(int, int) is not viable
在另一个问题的上下文中,相关的是函数模板的类型推导是否成功(并且进行了替换):
template<class T>
struct has_no_nested_type {};
// I think you need some specialization for which the following class template
// `non_immediate_context` can be instantiated, otherwise the program is
// ill-formed, NDR
template<>
struct has_no_nested_type<double>
{ using type = double; };
// make the error appear NOT in the immediate context
template<class T>
struct non_immediate_context
{
using type = typename has_no_nested_type<T>::type;
};
template<class T>
typename non_immediate_context<T>::type
foo(T, int) { return {}; }
template<class T>
bool foo(T) { return {}; }
int main()
{
foo(42); // well-formed? clang++3.5 and g++4.8.2 accept it
foo<int>(42); // well-formed? clang++3.5 accepts it, but not g++4.8.2
}
当为T == int
实例化第一个函数模板foo
时,替换会产生一个不在foo
直接上下文中的无效类型。这会导致一个硬错误(这就是相关问题的内容)
然而,当让foo
推导其模板参数时,g++和clang++同意不发生实例化。正如Johannes Schaub解释的那样,这是因为存在"匹配错误"。
问题:什么是"匹配错误",在标准中是在哪里以及如何指定的?
可选问题:为什么g++的foo(42)
和foo<int>(42)
之间存在差异?
到目前为止我发现/尝试了什么:
[over.match。/7和[temp.over]似乎描述了函数模板的重载解析细节。后者似乎强制要求用模板参数替换foo
。
有趣的是,[over.match。/7在检查函数模板(专门化)的可行性之前触发[temp.over] 中描述的过程。类似地,类型演绎不考虑,比如说,默认函数参数(除了使它们成为非演绎的上下文)。据我所知,这似乎与生存能力无关。
另一个可能重要的方面是如何指定类型演绎。它作用于单个函数参数,但我不明白包含/依赖于模板参数的参数类型(如T const&
)和不依赖于模板参数的参数类型(如int
)之间的区别在哪里。
然而,g++在显式指定模板形参(硬错误)和让它们被推导(推导失败/SFINAE)之间做出了区别。为什么?
我总结的是14.8.2.1p1
所描述的过程模板实参推导是通过将每个函数模板形参类型(称其为p)与调用的相应实参类型(称其为A)进行比较来完成的,如下所述。
在我们的例子中,对于p我们有(T, int)
,对于A我们有(int)
。对于第一对P/A,即T
对int
,我们可以将T
与int
匹配(通过14.8.2.5中描述的过程)。但对于第二"对",我们有int
,但没有对应的。因此,不能对这个"对"进行演绎。
因此,根据14.8.2.5p2,"如果不能对任何p/A对进行类型演绎,…、模板参数演绎失败".
这样你就不用在函数模板中替换模板参数了。
这些可能都可以在标准(IMO)中更精确地描述,但我相信这是如何实现与Clang和GCC的实际行为相匹配的东西,这似乎是对标准的合理解释。
- 类型演绎 C++ 标准和自动
- λ类型演绎失败
- decltype(auto) 类型演绎:返回 x 与返回 (x)
- 使用decltype的动态多态类型演绎
- 普遍类型演绎scott-meyers
- c++ 11使用std::函数进行类型演绎
- 使用通用引用时进行类型演绎
- c++如何在无法进行类型演绎时调用模板化构造函数
- c#泛型中的类型演绎类似于c++模板
- 类型演绎不适用于std::function
- 为什么decltype返回类型在递归模板中失败,而返回类型演绎却工作得很好?
- 表达式模板中的按引用捕获可以与类型演绎共存
- 类型演绎模板函数c++
- 模板,类型演绎不足
- 括号初始化列表和函数模板类型演绎顺序
- 类型演绎的重载赋值操作符
- 静态方法中派生类的类型演绎
- 在类型演绎之后,函数模板中的替换顺序是否有任何保证
- 返回类型演绎是否可能
- c++ 14中赋值时的返回类型演绎