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)
^