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

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)

但注释掉的行不会编译.

我的问题是:

  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 A.指出的那样,clang不会编译,但gcc4.9仍然会通过.但是,gcc4.2和gcc4.4都拒绝代码,并且他们抱怨的错误是"没有匹配的函数",而不是在clang中"调用没有对象的非静态成员".这个问题似乎没有确定的答案,所以我正在添加一个语言律师标签,正如Daniel Frey建议的那样.

更多更新:

我认为对于问题2,答案现在非常清楚:编译器是否添加静态声明会改变诊断.它因编译器和编译器以及同一编译器的不同版本而异.语言律师没有出现,我将接受Daniel Frey的回答,因为它最能解释第一个问题.但Marco A.和Hadi Brais的答案也值得一读.

Dan*_*rey 14

该标准仅要求在第一阶段解析时才进行名称查找.这变成了badfoobadbar这就是为什么代码编译.此时编译器不需要执行重载解析.

然后在实例化阶段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.


Col*_*mbo 7

考虑[temp.res]/8:

如果无法为模板生成有效的专业化,并且未实例化该模板,则模板格式错误,无需诊断.

这(特别是"无需诊断"位)使得任何编译器的行为都符合worsebar.实现在这种代码上的差异只是QoI问题 - 常见的编译器做了一些分析并会抱怨.很难说何时,您应该准备好在升级或切换实施时返回模板代码.