例如,下面的代码片段使用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;
} 但是注释掉的行不会编译。
我的问题是:
badbar()编译,但worsebar()不编译?badbar()更改为静态,它也不会编译,不管base::badfoo是否是静态的。badbar()。更新:
问题1得到了许多答案的解释,但编译器似乎有时也会加倍努力来检查过载,gcc 4.4.3和4.8.2会遇到这种情况,但4.7.2和4.9.1不会发生。
问题2:正如Marco .指出的那样,clang不会编译,但是gcc4.9仍然会通过。但是,gcc4.2和gcc4.4都拒绝代码,他们抱怨的错误是“没有匹配函数”,而不是在clang中“调用无对象的非静态成员”。这个问题似乎没有决定性的答案,所以我加入了一个语言律师标签,正如丹尼尔·弗雷所建议的。
更多最新情况:
我认为问题2的答案现在很清楚:是否添加静态声明将改变诊断取决于编译器。它因编译器和同一编译器的不同版本而异。语言律师没有出现,我将接受丹尼尔·弗雷的回答,因为它最好地解释了第一个问题。但马可·A·和哈迪·布雷斯的回答也值得一读。
发布于 2015-05-14 08:43:32
考虑tem.res/8:
如果不能为模板生成有效的专门化,并且没有实例化该模板,则模板的格式不正确,不需要诊断。
这(特别是“不需要诊断”bit)使得任何编译器的行为都符合worsebar。实现在这类代码上的差异只是QoI问题--普通编译器会做一些分析,并会抱怨。很难说具体时间,在升级或切换实现时,您应该准备返回模板代码。
发布于 2015-05-14 10:10:46
弄清楚些..。这个版本的代码在clang和gcc上编译得很好。
class Base
{
public:
static void foo() {}
void badfoo(int a) {}
};
template <typename T>
class Derived : public Base
{
public:
void bar() { Base::foo(); }
void badbar() { Base::badfoo(); }
}; 因为
Tem.res/p8
如果不能为模板生成有效的专门化,并且没有实例化该模板,则模板的格式不正确,不需要诊断。
gcc和clang 都不需要诊断这种。这个也与上面的情况相同(clang发出一个错误,gcc没有)
class Base
{
public:
static void foo() {}
void badfoo(int a) {}
};
template <typename T>
class Derived : public Base
{
public:
void bar() { Base::foo(); }
static void badbar() { Base::badfoo(); }
}; 案中
void worsebar() { Base::nonexist(); }与不同,因为它违反了名称查找tem.res/p9
当查找模板定义中使用的名称的声明时,通常的查找规则(3.4.1,3.4.2)用于非依赖的名称。
和温度/p10
如果名称不依赖于模板参数(如14.6.2中所定义),则该名称的声明(或一组声明)应在该名称出现在模板定义的位置上。
免责声明:以上所有内容都不适用于MSVC,MSVC很高兴地将所有这些内容推迟到查找的第二阶段。
编辑:
在这种情况下
class Base
{
public:
static void foo() {}
void badfoo(int i) {}
};
template <typename T>
class Derived : public Base
{
public:
static void badbar() { Base::badfoo(); } // static functionclang触发错误和gcc不。这是第一种情况,因为名称查找是成功的,但是clang执行额外的检查:因为badfoo是一个成员函数,所以它试图构造一个有效的隐式成员引用表达式。然后,它捕获一个成员函数被静态函数隐式调用的事实,并检测上下文不匹配。在这一点上,这个诊断完全取决于编译器(在实例化的情况下不会)。
发布于 2015-05-14 09:23:25
在实例化任何类型或发出任何代码之前,编译器将逐步构建一个已声明的所有符号的表。如果使用了未声明的符号,则会发出错误。这就是为什么worsebar不编译。另一方面,badfoo已经被声明,所以badbar编译。在编译过程的早期阶段,编译器将不检查对badfoo的调用是否与声明的badfoo实际匹配。
由于派生类型在代码中的任何地方都没有实例化,编译器将不会发出任何有关它的代码。特别是,坏酒吧将被忽视。
现在,当您声明派生实例(如Derived< int >),但不使用它的任何成员时,编译器将只创建一个已使用的成员的类型,而忽略其他成员。不过,没有关于坏酒吧的错误。
但是,在声明派生实例并调用badbar时,将需要对badbar方法进行实例化,因此编译器将使用badbar创建一个类型并对其进行编译。这一次,编译器注意到badfoo并没有被实际声明,因此会发出一个错误。
这种行为在14.7.1节的C++标准中有记录。
除非类模板或成员模板的成员已显式实例化或显式专门化,否则当专门化在要求成员定义存在的上下文中引用时,成员的专门化将被隐式实例化。
最后,如果badbar是静态的,并且是由编译器实例化的(因为它已经被使用了),那么编译器将发出一个错误,而badfoo并不存在。现在,如果将整数参数传递给badfoo,则会发出另一个错误,指示静态方法无法访问实例成员,因为首先没有实例。
编辑
编译器不必报告非实例化模板类型中的语义错误。标准只是说它不需要,但它可以。关于在哪里划线的问题还有待商榷。请参阅这对clang中相关问题的讨论:
我们分析哪些未实例化的模板?出于性能原因,我认为我们不应该分析所有未实例化的模板,因为我们可能会发现自己反复分析了很大一部分Boost和STL,等等。
因此,非实例化模板在分析不同版本的clang和gcc时会有不同的变化。但是,同样,按照标准:当然,不需要在未实例化的模板中报告错误。
https://stackoverflow.com/questions/30232485
复制相似问题