C++ 为什么我可以在没有转发声明的情况下调用函数模板?

C++ 为什么我可以在没有转发声明的情况下调用函数模板?,c++,function,templates,gcc,forward-declaration,C++,Function,Templates,Gcc,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); } void foo(int x) { bar(x);//错误:尚未声明bar } 空栏(整数x) { st

如果一个普通函数调用一个尚未声明的函数,我会得到一个编译时错误:

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);
}
void foo(int x)
{
bar(x);//错误:尚未声明bar
}
空栏(整数x)
{

std::cout函数模板不是函数;它是在模板参数已知后生成函数的方法

当编译器看到
foo
模板定义时,它无法查找
bar
的含义,因为它的含义可能取决于t是什么。因此它只记得有一个名称
bar
的用法需要稍后解决

当您调用
foo(42)
时,编译器必须生成(实例化)真正的函数,此时它会查找以前无法找到的名称,找到您的条形模板(并触发该模板的实例化),一切正常

对于普通函数,定义函数时可以查找所有名称,因此必须在该点正确声明它们。

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

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

你可能还想用谷歌搜索“两阶段查找”更多信息。只有最新版本的GCC才能正确执行这些规则,因为在解析模板的第一阶段还需要进行一些检查。正如Yakk指出的那样,较新版本的GCC会拒绝您的代码,因此务必使用最新版本的GCC或Clang进行检查,以确保安全。

这是一个错误“为什么天空是由砖块构成的”类型问题。Ie,一个问题,问为什么一些错误是真的。C++中的代码不是合法的。 ,正如您在GCC4.8中看到的,这实际上并不编译

我想“gcc 4.6为什么让这段代码编译”这个问题仍然存在。早期编译器在编写
模板
扩展器时所做的一件事是将它们视为类似于宏的东西。声明它们时所做的事情很少,在实例化它们时会查找所有内容

当代码< >代码> >代码时,编译器现在倾向于做更多的事情,而在实例化时则更少。这是C++标准所要求的,或者至少是更接近的。

恰巧,ADL可以绕过这一点:
bar
通过ADL查找
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)
      ^
prog.cpp:void foo(T)[with T=int]的实例化中:
程序cpp:16:7:从这里开始需要
prog.cpp:6:10:错误:“bar”未在此作用域中声明,并且在实例化点[-fppermissive]通过参数相关查找未找到任何声明
bar(x);//好的
^
prog.cpp:10:6:注:此处声明了“模板无效条(T)”,稍后在翻译单元中声明
空心钢筋(T x)
^

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

gcc 4.8.1。此处不编译。@sbabbi:有什么错误?它在gcc 4.8.1上编译得很好。@J.N错误:“bar”未在此作用域中声明,并且在实例化点[-fpermissive]通过依赖参数的查找未找到任何声明。“您是否按原样复制了示例?或者是否更改了任何内容?@J.N.”#include"还有复制粘贴。它也无法在ideone上编译。我很喜欢这个答案,但我承认它实际上并不正确——或者至少不完全正确。正如雅克在他的答案中指出的,规则更复杂——在实例化时查找名称时只考虑ADL,并且使用
int
不会触发ADL查找e全局名称空间,在该名称空间中可以找到
bar
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)
      ^