首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >编译器检查未实例化的模板代码是什么?

编译器检查未实例化的模板代码是什么?
EN

Stack Overflow用户
提问于 2015-05-14 08:14:51
回答 4查看 1.5K关注 0票数 29

例如,下面的代码片段使用gcc-4.9和clang-602编译

代码语言:javascript
复制
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;                                                                   
}  

但是注释掉的行不会编译。

我的问题是:

  1. 为什么badbar()编译,但worsebar()不编译?
  2. 如果我将badbar()更改为静态,它也不会编译,不管base::badfoo是否是静态的。
  3. 标准中有没有提到在这种情况下应该检查什么?实际上,gcc4.4甚至拒绝编译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·和哈迪·布雷斯的回答也值得一读。

EN

回答 4

Stack Overflow用户

发布于 2015-05-14 08:43:32

考虑tem.res/8:

如果不能为模板生成有效的专门化,并且没有实例化该模板,则模板的格式不正确,不需要诊断。

这(特别是“不需要诊断”bit)使得任何编译器的行为都符合worsebar。实现在这类代码上的差异只是QoI问题--普通编译器会做一些分析,并会抱怨。很难说具体时间,在升级或切换实现时,您应该准备返回模板代码。

票数 7
EN

Stack Overflow用户

发布于 2015-05-14 10:10:46

弄清楚些..。这个版本的代码在clang和gcc上编译得很好。

代码语言:javascript
复制
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没有)

代码语言:javascript
复制
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(); } 
}; 

案中

代码语言:javascript
复制
void worsebar() { Base::nonexist(); }

不同,因为它违反了名称查找tem.res/p9

当查找模板定义中使用的名称的声明时,通常的查找规则(3.4.1,3.4.2)用于非依赖的名称。

和温度/p10

如果名称不依赖于模板参数(如14.6.2中所定义),则该名称的声明(或一组声明)应在该名称出现在模板定义的位置上。

免责声明:以上所有内容都不适用于MSVC,MSVC很高兴地将所有这些内容推迟到查找的第二阶段。

编辑:

在这种情况下

代码语言:javascript
复制
class Base                                                                      
{                                                                               
public:                                                                         
    static void foo() {}                                                        
    void badfoo(int i) {}                                                       
};                                                                              

template <typename T>                                                           
class Derived : public Base                                                     
{                                                                               
public:                                                                         
    static void badbar() { Base::badfoo(); }  // static function

clang触发错误gcc不。这是第一种情况,因为名称查找是成功的,但是clang执行额外的检查:因为badfoo是一个成员函数,所以它试图构造一个有效的隐式成员引用表达式。然后,它捕获一个成员函数被静态函数隐式调用的事实,并检测上下文不匹配。在这一点上,这个诊断完全取决于编译器(在实例化的情况下不会)。

票数 3
EN

Stack Overflow用户

发布于 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时会有不同的变化。但是,同样,按照标准:当然,不需要在未实例化的模板中报告错误。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/30232485

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档