C++ 为什么C++;绝不允许在函数启用之前使用它们';重新申报?

C++ 为什么C++;绝不允许在函数启用之前使用它们';重新申报?,c++,c++11,compiler-construction,declaration,C++,C++11,Compiler Construction,Declaration,好的,我知道这看起来像是一个复制品,但似乎现有的答案并没有完全解决所有的细节 我知道C++最初是在80的时候设计的,所以它可以在一次翻译中被翻译,因为计算机很慢。好啊但是最近的标准发布在2011,所以我不明白为什么C++编译器现在不能做需要多次传递的事情。这仍然会影响性能,是的,但只有在必要的时候。因此,以下操作仍然只需要一次通过: void foo(); int main() { foo(); } void foo() {} 而对于以下情况,编译器可以生成两个(并且速度较慢),因为在看到下面

好的,我知道这看起来像是一个复制品,但似乎现有的答案并没有完全解决所有的细节

我知道C++最初是在80的时候设计的,所以它可以在一次翻译中被翻译,因为计算机很慢。好啊但是最近的标准发布在2011,所以我不明白为什么C++编译器现在不能做需要多次传递的事情。这仍然会影响性能,是的,但只有在必要的时候。因此,以下操作仍然只需要一次通过:

void foo();
int main() { foo(); }
void foo() {}
而对于以下情况,编译器可以生成两个(并且速度较慢),因为在看到下面的声明之前,它不知道
foo
是函数还是类型:

int main() { foo(); }
void foo() {}
如果您试图在没有先声明函数的情况下使用该函数,并且声明根本不在当前翻译单元中,这将是一个错误。但是如果它在同一个翻译单元中,那么编译器就可以进行额外的传递


我的同事认为,这样一个特性将节省大量开发人员的时间,并将避免声明和定义不匹配的问题。我相信这已经被提出过很多次了,每次都被拒绝了。拒绝它背后的实际理由是什么,即委员会的理由?

目前的答案是因为它是不可辩驳的

考虑模板的两阶段名称查找,尤其是。在模板中,可能尚未声明类型相关名称。为了能够解析它们,我们绝对需要
typename
。否则,解析将陷入停顿,我们无法可靠地继续,因此我们甚至无法提供修复解析问题所需的类型。这是一个鸡和蛋的问题:如果我们需要解析第10行来解析第5行,那么第10行永远不会被解析,因为我们在第5行中断
typename
帮助我们通过第5行,这样我们就可以在第10行了解实际的类型

在这里,我们会遇到类似的问题。在你的假设下考虑这个代码:

struct Foo { };
int bar () { return Foo(); }
int Foo () { return 42; }
要解析此代码,我们需要知道
Foo
是否表示类型或函数。

模板解析 考虑以下代码行:

a < b , c > d;
a
是已知模板类型的情况下,
b
c
是其他已知类型。第二,

   a<b   ,   c<d ;
   ^^^       ^^^ 
boolean expressions
这可以是函数声明(返回类型为
a
且参数类型为
c
)或变量定义(类型为
a
,构造函数参数为
c

结论 不幸的是,有很多类似的东西。我不确定,是否不可能编写一个能够处理这类内容的编译器,但至少编写一个编译器是非常困难的。编译时间是C++中的一个严肃问题。这只会让事情变得更糟。另外:最好只使用您已经定义或声明的内容,即使是在其他语言中

委员会的限制
即使可以合理地实现此功能,它也会破坏向后兼容性。函数重载解析只考虑先前的声明,函数调用的解释可能会根据函数调用的编写位置而改变。这就是C++的组合方式。现在,C++标准委员会是一个大的后台兼容性。特别是:他们不想破坏任何现有的代码(这是正确的)。你的建议肯定会打破现有的代码,这对于语言设计者来说是不可能的

我怀疑委员会是否考虑过,因为C++在很早就开始执行类型安全链接(标准化过程开始很久以前)。而且,匹配参数并将参数转换为可用函数原型的过程已经非常困难,而不必等到TU结束时才能解析代码中的第一个函数调用(因为编译器必须收集每个函数的所有函数类型信息,才能为任何函数调用生成任何代码,所以它将强制进行两次传递处理)。现在添加它可能会破坏有效代码。稍后可以定义更匹配的重载,在引入此功能时更改程序行为。@JonathanLeffler
int main(){int x=foo(1);}typedef int foo;
归根结底,打破25年的现状是没有意义的——到目前为止,它可能比这还要长。坦率地说,这是一个真的不需要问的问题。它有什么好处?你确定它不会产生意料之外的后果吗?我几乎可以保证它是意料之外的后果,以及不希望出现的意外后果。@EJP:wat。大多数现代语言都依赖编译器进行多次传递。你知道计算机科学在60年代极力避免这种情况的原因吗?这不是因为纯净、优雅或良好的理论优势。这是因为它们没有足够的内存来加载所有的数据ce代码。因此,他们必须逐步加载代码,并在完成后卸载旧代码块。在这种情况下,在一次传递中完成所有操作都很有意义。他们根本无法回顾已经编译的代码。2014年?没有那么多。我可以看出鸡和蛋的问题可能是有问题的,但我看不出您的示例如何为我的问题说明这一点。首先,可能有一条规则,根据前面的声明解释名称,因此在这种情况下,
bar
中的
Foo
将明确地成为一个类型。其次,即使编译器不知道
Foo
是一个函数还是一个类型,它仍然可以找到终止分号,并设法解析
Foo
声明后面的
ba声明
   a<b   ,   c<d ;
   ^^^       ^^^ 
boolean expressions
a b(c); // is 'b' a function or a variable?