我很好奇为什么C ++没有定义void via:
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
来完成同样的技巧,因为void不能假设任何值,甚至不是空值。这种区别有没有用处?
还有其他各种强类型语言,比如Haskell,可能是ML,它们的void类型有一个值,但是没有公开提供任何无价值的类型,尽管有些类型拥有本机指针类型,就像void *
一样。
答案 0 :(得分:4)
我看到void
无法从C ++的C根实例化的基本原理。在古老的血腥日子里,类型安全并不是什么大不了的事,void*
经常被传递。但是,你总是可以看到代码不是字面上void*
(由于typedef
s,宏,在C ++中的模板),但仍然意味着它。
如果您不能取消引用void*
但必须先将其转换为指向正确类型的指针,这是一点点安全。是否通过使用不完整类型Ben Voigt建议,或者使用内置void
类型无关紧要。您受到保护,不会错误猜测您正在处理的类型,而您不是。
是的,void
引入了令人讨厌的特殊情况,尤其是在设计模板时。但是编译器不会静默接受实例化void
的尝试是一件好事(即有意的)。
答案 1 :(得分:3)
因为这不会使void
成为不完整的类型,现在会吗?
再次尝试使用以下示例代码:
struct x; // incomplete
typedef x moo;
答案 2 :(得分:3)
为什么
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
应该是完整的类型?”