考虑以下程序:
template <class T> struct A { using X = typename T::X; };
template <class T, typename A<T>::X* = nullptr> void f(T, int);
void f(...);
template <class T> void g(T, int, typename A<T>::X* = nullptr); // #
void g(...);
int main() {
// f(0, nullptr); // error
g(0, nullptr); // ok
}
g(0, nullptr)
编译而f(0, nullptr)
不编译(在Godbolt的GCC干线和Clang干线下测试)。似乎在#
的模板参数推导过程中,编译器在发现参数A<int>
与参数nullptr
不匹配时不会实例化int
。标准在哪里指定这种行为?
答案 0 :(得分:4)
这是CWG1391:
如果对包含以下所有参数的推导成功 参与模板参数推导的 template-parameters ,所有模板参数均已明确指定,推导或 从默认模板参数获取,其余参数为 然后与相应的参数进行比较。对于其余的 参数
P
的类型在替换之前是非依赖的 任何显式指定的模板参数的值(如果有) 参数A
无法隐式转换为P
,推导失败。
答案 1 :(得分:3)
您可能被DR #1844所咬。在[temp.deduct] / 8中指出:
如果替换导致无效的类型或表达式,则类型推导失败。如果使用替换的参数编写,则无效的类型或表达式将是格式错误的,并且需要诊断。 [注意:如果不需要诊断,则程序仍然格式错误。 访问检查是替代过程的一部分。 —注释[注释]仅在函数类型,其模板参数类型及其显式说明符的直接上下文中无效的类型和表达式可能导致推论失败。 [注:替换为类型和表达式可能会导致诸如实例化类模板专业化和/或函数模板专业化,生成隐式定义的函数等效果。此类效果不在“立即上下文”中,并且可以导致程序格式错误。 —注释]
这里的问题是“即时上下文”没有真正定义,导致编译器之间出现差异。