swa*_*ang 29 c++ gcc templates clang language-lawyer
例如,以下代码段使用gcc-4.9和clang-602进行编译
class Base
{
public:
static void foo() {}
void badfoo(int i) {}
};
template <typename T>
class Derived : public Base
{
public:
void bar() { Base::foo(); }
void badbar() { Base::badfoo(); } // compiles ok
//static void badbar() { Base::badfoo(); } // compile error
//void worsebar() { Base::nonexist(); } // compile error
};
int main()
{
return 0;
}
Run Code Online (Sandbox Code Playgroud)
但注释掉的行不会编译.
我的问题是:
为什么badbar()
编译但worsebar()
不编译?
如果我badbar()
改为静态,它也不会编译,无论是否base::badfoo
是静态的.
标准是否说明了在这种情况下应该检查什么?gcc4.4实际上甚至拒绝编译badbar()
.
更新:
问题1已被许多答案解释,但似乎编译器有时会更加努力地检查过载,它发生在gcc 4.4.3和4.8.2,而不是4.7.2和4.9.1.
问题2:正如Marco A.指出的那样,clang不会编译,但gcc4.9仍然会通过.但是,gcc4.2和gcc4.4都拒绝代码,并且他们抱怨的错误是"没有匹配的函数",而不是在clang中"调用没有对象的非静态成员".这个问题似乎没有确定的答案,所以我正在添加一个语言律师标签,正如Daniel Frey建议的那样.
更多更新:
我认为对于问题2,答案现在非常清楚:编译器是否添加静态声明会改变诊断.它因编译器和编译器以及同一编译器的不同版本而异.语言律师没有出现,我将接受Daniel Frey的回答,因为它最能解释第一个问题.但Marco A.和Hadi Brais的答案也值得一读.
Dan*_*rey 14
该标准仅要求在第一阶段解析时才进行名称查找.这变成了badfoo
在badbar
这就是为什么代码编译.此时编译器不需要执行重载解析.
然后在实例化阶段2中执行重载决策(在名称查找后始终作为单独的步骤发生)badbar
- 在您的示例中不是这种情况.这个原则可以在
3.4名称查找[basic.lookup]
1名称查找规则统一适用于所有名称(包括typedef-names(7.1.3),namespace-names(7.3)和class-names(9.1)),只要语法允许在特定规则讨论的上下文中使用这些名称.名称查找将名称的使用与该名称的声明(3.1)相关联.名称查找应找到名称的明确声明(见10.2).如果名称查找名称是函数名称,则名称查找可以将多个声明与名称相关联; 据说声明形成一组重载函数(13.1).名称查找成功后,将发生重载分辨率(13.3).访问规则(第11条)仅在名称查找和功能重载解析(如果适用)成功后才被考虑.只有在名称查找之后,函数重载解析(如果适用)和访问检查成功才会在表达式处理(第5章)中进一步使用名称声明引入的属性.
(强调我的)
因此,我会说编译器接受代码是正确的,尽管我不确定他们是否需要这样做.
要查看被拒绝的代码,您需要实例化badbar
.
考虑[temp.res]/8:
如果无法为模板生成有效的专业化,并且未实例化该模板,则模板格式错误,无需诊断.
这(特别是"无需诊断"位)使得任何编译器的行为都符合worsebar
.实现在这种代码上的差异只是QoI问题 - 常见的编译器做了一些分析并会抱怨.很难说何时,您应该准备好在升级或切换实施时返回模板代码.