是不是在实例化时编译的模板类成员函数?

Shi*_*rik 5 c++ gcc visual-studio

将代码从Visual Studio移植到gcc时,我发现了一个奇怪的问题.以下代码在Visual Studio中编译良好,但导致gcc中出错.

namespace Baz
{
   template <class T>
   class Foo
   {
   public:
      void Bar()
      {
         Baz::Print();
      }
   }; 

   void Print() { std::cout << "Hello, world!" << std::endl; }
}

int main()
{
   Baz::Foo<int> foo;
   foo.Bar();

   return 0;
}
Run Code Online (Sandbox Code Playgroud)

我的理解是这应该编译好,因为在实例化模板之前不应该编译类(在定义了Print()之后).但是,gcc报告如下:

t.cpp:在成员函数'void Baz :: Foo :: Bar()'中:第8行:错误:'Print'不是'Baz'的成员

谁是对的?如果gcc是对的,为什么?

Ant*_*ams 8

gcc是对的.模板分两个阶段编译:一次解析时,一次实例化.

在分析时,会检查不依赖于模板参数的任何内容,因此在这种情况下会Baz::Print被查找,并且发现没有声明,所以这是一个错误.

如果调用已经T().Print()或依赖于类型的其他东西,T那么查找将被延迟直到实例化时间(或者至少部分延迟---见下文).

除非资格本身依赖于模板参数(例如T::Print),否则Baz :: Print等合格的名称总是在定义时查找,尽管重载决策被推迟到实例化时间.这意味着在声明模板后,您无法添加限定名称的重载集.

Print如果任何函数参数依赖于模板参数,则会在实例化时查找非限定名称(例如just).所以Print(T())会在实例化时查看,而Baz::Print(T())不是.值得注意的是,实例化时间查找仅限于在声明点处可见的名称和通过ADL找到的名称,因此Foo<int>,即使是普通Print(T())也不应该找到Baz::Print它是否在模板之后声明(如示例中所示).

要使示例代码有效,可以在定义Foo<T>::Bar之后定义,也可以在定义之前定义前Print向声明.PrintFoo


jpa*_*cek 4

海湾合作委员会是对的。这是因为Baz是一个命名空间,并且命名空间是从上到下解析的,因此 的声明Baz::Print从内部不可见Foo(因为它位于其下方)。

实例化模板时,仅考虑模板定义中可见的名称,不计算 Koenig 查找(这不会改变您的情况)。

如果Baz是一个结构或类,您的代码将起作用,因为它们分两个阶段进行解析(首先是声明,然后是主体),因此在结构或类中声明的任何内容在例如内部都是可见的。成员函数,无论它们在源文件中的顺序如何。

Baz::Print您可以通过在 before声明来使其工作Foo

引用标准:

14.6.3 非从属名称

模板定义中使用的非依赖名称是使用通常的名称查找找到的,并在使用它们时绑定。

14.6.4 依赖名称解析

在解析从属名称时,会考虑以下来源的名称:

  • 在模板定义时可见的声明。
  • 与来自实例化上下文 (14.6.4.1) 和定义上下文的函数参数类型关联的命名空间的声明。

(报价结束)

Print是非依赖的(就像现在一样)时,不会找到它,因为它是在其声明之前(在模板定义上下文中)查找的。如果它是依赖的,则它不会是第一种情况(与非依赖时相同),并且Baz不以任何方式与 int (模板参数)关联,因此也不会根据第二种情况进行搜索。