为什么我可以调用没有前向声明的函数模板?

fre*_*low 4 c++ gcc templates function forward-declaration

如果普通函数调用尚未声明的函数,则会出现编译时错误:

void foo(int x)
{
    bar(x);   // ERROR: bar has not been declared yet
}

void bar(int x)
{
    std::cout << x << '\n';
}

int main()
{
    foo(42);
}
Run Code Online (Sandbox Code Playgroud)

修复方法是向前声明被调用的函数,或者切换定义的顺序.

但是,功能模板似乎不需要这些修复:

template<typename T>
void foo(T x)
{
    bar(x);   // OKAY
}

template<typename T>
void bar(T x)
{
    std::cout << x << '\n';
}

int main()
{
    foo(42);
}
Run Code Online (Sandbox Code Playgroud)

编译得很好.这是为什么?当编译器看到时bar(x),为什么不抱怨?

(我使用的是g ++ 4.6.3)

Yak*_*ont 10

这是一个"为什么用砖砌成的天空"式问题.即,一个问题,为什么一些错误是真的.事实并非如此,在C++中您的代码是合法的.

实例,正如您在gcc 4.8中看到的那样,实际上并没有编译.

我猜这个问题"为什么gcc 4.6让这个代码编译"仍然存在.编译器在编写template扩展器时早期做的事情之一是将它们视为与宏类似的东西.当声明它们时,它们会很少被完成,并且当它们被实例化时,所有内容都会被查找.

编译器现在倾向于在template声明时执行更多操作,而在实例化时则更少.这就是C++标准所要求的,或者至少更接近.

碰巧的是,ADL可以解决这个问题:通过ADL bar查找的查找bar不必在foo写入的位置可见,而是在实例化时.

gcc 4.8错误消息非常自我解释:

prog.cpp: In instantiation of ‘void foo(T) [with T = int]’:
prog.cpp:16:7:   required from here
prog.cpp:6:10: error: ‘bar’ was not declared in this scope, and no declarations were found by argument-dependent lookup at the point of instantiation [-fpermissive]
     bar(x);   // OKAY
          ^
prog.cpp:10:6: note: ‘template<class T> void bar(T)’ declared here, later in the translation unit
 void bar(T x)
      ^
Run Code Online (Sandbox Code Playgroud)

这些要求可能已在C++ 11中进行了更改或澄清,因此gcc 4.6的行为可能在C++ 03标准下是合法的.


Dan*_*rey 9

当编译器第一次看到bar(x)它时,它不知道x类型,因此无法查找正确的类型bar.只有在实例化时foo,T因此x类型是已知的并且bar(x)可以被查找.

请注意,这仅适用于依赖表达式,即依赖于模板参数的表达式.如果添加bar(42),即使稍后将其实例化,也将无法编译T==int.

您可能还想谷歌"两阶段查找"以获取更多信息.只有最新版本的GCC才能正确实现这些规则,因为在解析模板的第一阶段还需要进行一些检查.作为Yakk的指针,较新版本的GCC会拒绝您的代码,因此请始终使用最新版本的GCC或Clang进行检查以确保安全.