现在很多次,我在C ++中遇到了声明和定义顺序的问题:
struct A {
void Test() { B(); }
};
void B() {
A a;
}
当然,这可以通过预先B()
来解决。 通常这足以解决任何这些问题。但是当使用基于模块的仅头文件库或类似复杂的包含系统时,这种声明/定义概念可能非常痛苦。我在下面提供了一个简单的例子。
如今,大多数现代语言编译器对源文件进行两次传递,以在第一次传递中构建声明,并在第二次传递中处理定义。将此方案引入C ++也不应该破坏任何旧代码。因此,
这是基于模块的头库的示例,由于缺少预先声明,因此包含阻塞。为了解决这个问题,图书馆的用户必须预先确定"缺失"类,这是不可行的。 当然,这个问题可以通过使用在定义之前命令所有声明的公共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(); }
str_usage.cpp
#include "string.h"
int main() {
String str;
}
答案 0 :(得分:2)
void f(int);
void g() { f(3.14); }
void f(double);
g
目前正在调用f(int)
,因为它是f
唯一可见的。它在你的世界中叫什么?
f(double)
,那么您只需打破大量现有代码。如果你提出一些规则让它仍然调用f(int)
,那就意味着我写了
void g2() { f2(3.14); }
void f2(double);
然后为参数引入更糟糕的匹配 - 比如void f2(int);
之前的g2
,g2
会突然开始调用错误的东西。这是可维护性的噩梦。
答案 1 :(得分:1)
一个更简单的解决方案是将类定义与函数定义分开:
struct A {
void Test();
};
struct B {
A a;
};
inline void A::Test() {
B();
}
答案 2 :(得分:0)
C ++语法中存在歧义,只有在知道标识符引用的内容时才能解决。
例如:
a * b;
如果a
是变量,则可以是乘法,如果a
是类型,则可以是指针声明。这些中的每一个都会导致不同的解析树,因此解析器必须知道a
是什么。
这意味着解析和名称解析不能在单独的传递中执行,但必须在一次传递中完成,这导致需要预先声明名称。