为什么 void 在 C++ 中不取 void 值?

Why doesn't void take a void value in C++?

本文关键字:void C++ 为什么      更新时间:2023-10-16

我很好奇为什么C++不通过以下方式定义 void:

typedef struct { } void;

即,即使该安装必须不生成代码,也无法实例化的类型中的值是多少?

如果我们使用 gcc -O3 -S ,那么以下两个都会生成相同的汇编程序:

int main() { return 0; }

template <class T> T f(T a) { }
typedef struct { } moo;
int main() { moo a; f(a); return 0; }

这是完全有道理的。 struct { }只是取一个空值,很容易优化。 实际上,奇怪的部分是它们在没有-O3的情况下生成不同的代码。

然而,你不能简单地用typedef void moo来拉出同样的把戏,因为空不能假设任何价值,即使是空的。 这种区别有什么用处吗?

还有其他各种强类型语言,如 Haskell,大概还有 ML,它们的 void 类型具有值,但没有公开提供无价值类型,尽管有些拥有本机指针类型,它们的行为类似于void *

我认为void无法实例化的理由来自C++的C根。在过去血腥的日子里,类型安全并不是什么大问题,void*不断被传递。但是,您可能总是看到的代码没有字面意思说void*(由于typedef、宏和C++中的模板(,但仍然有意义。

如果您不能取消引用void*但必须首先将其强制转换为指向正确类型的指针,则这是一点点安全性。无论你是按照 Ben Voigt 的建议使用不完整的类型来实现,还是使用内置的void类型都无关紧要。您可以避免错误地猜测您正在处理一个类型,而您不是。

是的,void引入了烦人的特殊情况,尤其是在设计模板时。但是编译器不会默默接受实例化void的尝试是一件好事(即故意的(。

因为这

不会使void成为一个不完整的类型,现在呢?

使用以下命令再次尝试示例代码:

struct x; // incomplete
typedef x moo;

为什么void应该是不完整的类型?

原因有很多。

最简单的是:moo FuncName(...)仍然必须返回一些东西。无论是中性值,还是"非值值",或者其他什么;它仍然必须说return value;,其中value是一些实际值。它必须说return moo();.

为什么要编写一个在技术上返回某些内容的函数,而它实际上并没有返回某些内容?编译器无法优化返回输出,因为它返回的是完整类型的值。调用方可能实际使用它。

你知道,C++并不全是模板。编译器不一定具有将所有内容都扔掉的知识。它通常必须执行函数内优化,而这些优化不知道该代码的外部使用。

在这种情况下,void的意思是"我不归还任何东西"。这和"我返回一个无意义的值"之间有一个根本的区别。这样做是合法的:

moo FuncName(...) { return moo(); }
moo x = FuncName(...);

这充其量是误导。粗略扫描表明正在返回和存储值。标识符x现在具有值,可用于某些内容。

而这:

void FuncName(...) {}
void x = FuncName(...);

是编译器错误。这也是这样的:

void FuncName(...) {}
moo x = FuncName(...);

很明显发生了什么:FuncName没有返回值。

即使您从头开始设计C++,没有来自 C 的保留,您仍然需要一些关键字来指示返回值的函数和不返回值的函数之间的区别。

此外,void*之所以特殊void部分原因是它不是一个完整的类型。由于标准要求它不是一个完整的类型,因此void*的概念可能意味着"指向非类型化内存的指针"。这具有专门的转换语义。指向类型化内存的指针可以隐式转换为非类型化内存。指向非类型化内存的指针可以显式转换回它以前的类型化指针。

如果你改用moo*,那么语义就会变得奇怪。您有一个指向类型化内存的指针。目前,C++将不相关的类型指针之间的强制转换(某些特殊情况除外(定义为未定义的行为。现在,标准必须为 moo 类型添加另一个例外。它必须说明当你这样做时会发生什么:

moo *m = new moo;
*moo;

对于void,这是一个编译器错误。moo是怎么回事?它仍然是无用和无意义的,但编译器必须允许它。

老实说,我会说更好的问题是"为什么void应该是一个完整的类型?