C++ 为什么声明/定义顺序在C++;?
现在,我在C++中多次遇到声明和定义顺序的问题:C++ 为什么声明/定义顺序在C++;?,c++,c++11,C++,C++11,现在,我在C++中多次遇到声明和定义顺序的问题: struct A { void Test() { B(); } }; void B() { A a; } 当然,这可以通过预先删除B()来解决。通常这足以解决这些问题。但是,当使用基于模块的仅头库或类似复杂的include系统时,这种声明/定义概念可能会非常痛苦。我在下面提供了一个简单的例子 现在大多数现代语言编译器对源文件进行两次遍历,第一次遍历构建声明,第二次遍历处理定义。在C++中引入这个方案也不应该破坏任何旧代码。所以
struct A {
void Test() { B(); }
};
void B() {
A a;
}
当然,这可以通过预先删除B()
来解决。通常这足以解决这些问题。但是,当使用基于模块的仅头库或类似复杂的include系统时,这种声明/定义概念可能会非常痛苦。我在下面提供了一个简单的例子
现在大多数现代语言编译器对源文件进行两次遍历,第一次遍历构建声明,第二次遍历处理定义。在C++中引入这个方案也不应该破坏任何旧代码。所以,
-
为什么没有这样或类似的方法已经被引入到C++中?
- 现行标准中是否有禁止这种方法的相关条款
例子 这是一个基于模块的标头库的示例,由于缺少predeclarations,它具有阻塞include。要解决这个问题,库的用户必须预先声明“缺少的”类,这是不可行的。 当然,这个问题可以通过使用一个公共include头来解决,该头在定义之前对所有声明进行排序,但是使用两次传递,这段代码也可以工作,不需要修改
oom.h
#pragma once
#include "string.h"
struct OOM {
String message;
};
string.h
#pragma once
#include "array.h"
struct String {
Array data;
};
array.h
#pragma once
struct Array {
void Alloc();
};
#include "oom.h"
void Array::Alloc() { throw OOM(); }
stru用法.cpp
#include "string.h"
int main() {
String str;
}
< C++语法中有歧义,只有知道标识符是指什么才能解决。 例如:
a * b;
如果a
是变量,则可以是乘法;如果a
是类型,则可以是指针声明。每种方法都会导致不同的解析树,因此解析器必须知道a
是什么
这意味着解析和解析不能在单独的传递中执行,但必须在一次传递中完成,导致预声明的要求。
< P>在C++文法中存在歧义,只有知道标识符是指的,才能解决。 例如:a * b;
如果a
是变量,则可以是乘法;如果a
是类型,则可以是指针声明。每种方法都会导致不同的解析树,因此解析器必须知道a
是什么
这意味着解析和名称解析不能在单独的过程中执行,而必须在一个过程中完成,因此需要预先声明名称。一个更简单的解决方案是将类定义与函数定义分开:
struct A {
void Test();
};
struct B {
A a;
};
inline void A::Test() {
B();
}
更简单的解决方案是将类定义与函数定义分开:
struct A {
void Test();
};
struct B {
A a;
};
inline void A::Test() {
B();
}
g
当前调用f(int)
,因为它是唯一可见的f
。在你的世界里它叫什么
- 如果它调用
,您就破坏了大量的现有代码f(double)
- 如果您提出了一些规则使它仍然调用
,那么这意味着如果我编写f(int)
然后为参数引入一个更差的匹配-比如,void g2() { f2(3.14); } void f2(double);
在void f2(int)
之前,g2
会突然开始调用错误的东西。那是一场噩梦g2
g
当前调用f(int)
,因为它是唯一可见的f
。在你的世界里它叫什么
- 如果它调用
,您就破坏了大量的现有代码f(double)
- 如果您提出了一些规则使它仍然调用
,那么这意味着如果我编写f(int)
然后为参数引入一个更差的匹配-比如,void g2() { f2(3.14); } void f2(double);
在void f2(int)
之前,g2
会突然开始调用错误的东西。那是一场噩梦g2
a
的类型的“挂起声明”时,这并不含糊。我的意思是,这不像是用户通过预先声明从两种含义中选择一种。代码一开始并不含糊<代码>a*b完全由上下文定义。是的,它可以有两种含义,但上下文决定它是哪一种。@nyronium否。在更现代的语言中,第一个过程从代码构造一个抽象语法树(AST),第二个过程执行名称解析。但是在这里,您不能在进行名称解析之前构建AST,因为解析器缺少信息。上面显示的歧义意味着解析器将不知道如何继续解析其余的代码。难道不能通过延迟这些歧义直到所有候选代码都得到解决吗?当没有可能创建名为a
的类型的“挂起声明”时,这并不含糊。我的意思是,这不像是用户通过预先声明从两种含义中选择一种。代码一开始并不含糊<代码>a*b完全由上下文定义。是的,它可以有两种含义,但上下文决定它是哪一种。@nyronium否。在更现代的语言中,第一个过程从代码构造一个抽象语法树(AST),第二个过程执行名称解析。但是在这里,您不能在进行名称解析之前构建AST,因为解析器缺少信息。上面显示的歧义意味着解析器不知道如何继续解析其余代码。