请考虑以下代码段:
struct Base { };
struct Derived : Base { };
void f(Base &) { std::cout << "f(Base&)\n"; }
template <class T = int>
void g() {
Derived d;
f(T{} ? d : d); // 1
}
void f(Derived &) { std::cout << "f(Derived&)\n"; }
int main() {
g();
}
在这种情况下,我认为应该在第一阶段查找f
// 1
的函数调用,因为它的参数类型是明确的Derived&
,并且因此被解析为f(Base&)
,这是范围内唯一的。{/ p>
Clang 3.8.0 agrees with me,但GCC 6.1.0 doesn't,并将f
的查询推迟至第二阶段,其中f(Derived&)
被选中。
哪种编译器是对的?
答案 0 :(得分:23)
使用最新版本的C++ standard目前n4582。
在第14.6节(第10页)中,如果名称不依赖于模板参数,则表示名称在声明点处被绑定。如果它取决于模板参数,则在第14.6.2节中定义。
第14.6.2.2节接着说,如果任何子表达式是类型相关的,则表达式是类型相关的。
现在,因为对f()
的调用取决于其参数。您查看参数类型以查看它是否取决于类型。参数为False<T>::value ? d : d
。这里第一个条件取决于类型T
。
因此我们得出结论,调用是在实例化而非声明的情况下绑定的。因此应绑定到:void f(Derived &) { std::cout << "f(Derived&)\n"; }
因此g ++具有更准确的实现。
14.6名称解析[temp.res]
第10段:
如果名称不依赖于模板参数(如14.6.2中所定义),则该名称的声明(或声明集)应在范围内。名称出现在模板定义中;该名称绑定到该点发现的声明(或声明),并且此绑定不受在实例化时可见的声明的影响。
14.6.2.2依赖于类型的表达式[temp.dep.expr]
除非如下所述,否则如果任何子表达式取决于类型,则表达式依赖于类型。
答案 1 :(得分:7)
我认为gcc(顺便提一下,视觉工作室)就是这个。
n4582,§14.6.2.2
除非如下所述,否则如果任何子表达式依赖于类型,则表达式依赖于类型。
在T{} ? d : d
中,有3个子表达式:
T{}
,显然是类型依赖d
(2次),不依赖于类型由于存在类型相关的子表达式,并且三元运算符未在§14.6.2.2的异常列表中进行计算,因此它被视为类型相关。
答案 2 :(得分:1)
根据c ++草案(n4582)§14.7.1.5:
除非已明确表示功能模板专业化 实例化或明确专门化的函数模板 专业化是在专业化时隐式实例化的 在需要函数定义存在的上下文中引用。 除非调用函数模板显式特化或者 一个显式专用类模板的成员函数,a 函数模板的默认参数或者函数的成员函数 调用函数时隐式实例化类模板 在需要默认参数值的上下文中。
我想说gcc对此更为正确。
例如,如果您创建void g()
的专用版本,则会获得both compiler doing the same。