【问题标题】:Why doesn't void take a void value in C++?为什么 void 在 C++ 中不采用 void 值?
【发布时间】:2012-04-02 09:07: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 来实现相同的技巧,因为 void 不能假定任何值,甚至不能假定为空值。这种区别有什么用处吗?

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

【问题讨论】:

    标签: c++ templates optimization types


    【解决方案1】:

    我看到 void 无法从 C++ 的 C 根实例化的原因。在过去的血腥日子里,类型安全并不是什么大问题,void*s 经常被传递。但是,您可能总是在查看不是字面意思的代码 void*(由于 typedefs,宏,在 C++ 中,模板),但仍然意味着它。

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

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

    【讨论】:

    • 除最后一段外,我几乎同意所有内容。它在处理模板时引入的烦人的特殊情况是故意的?做什么的?惹恼程序员?
    • @R.MartinhoFernandes:嗯,不是故意让我们烦恼,而是故意让你的库能够处理它无法实例化的类型,@987654330 @ 是最突出的例子。例如,如果您编写了一个类型安全的 memcpy 模板,它基本上就是这样做的:*to = *from。然后插入void应该让编译器叫你名字,而不是默默地接受它,因为复制void的OPs虚拟值不会是你的想法!跨度>
    • 我承认这种历史观点是有道理的。如果模板不存在,那么您不考虑将类型变量专门用于不能包含数据的类型。相反,您担心人们会以不存在的价值观做傻事。
    • @R.MartinhoFernandes:我改写了那段的措辞。
    • 为了使void 在模板中更好地工作,ISO 委员会确实做了很多工作。例如。在template &lt;class T&gt; T bar() 中,即使T==voidreturn foo(); 语句也是合法的。这是编译器的特例,而不是用户。
    【解决方案2】:

    因为这不会使 void 成为不完整的类型,现在会吗?

    再次尝试您的示例代码:

    struct x; // incomplete
    typedef x moo;
    

    【讨论】:

    • moo 在我的示例中是不完整的类型吗?在我用过的很少的地方它工作得很好。
    • @Jeff:不,你的moo 是一个完整的类型。我的不完整。 void 是一个不完整的类型,永远无法完成。
    • void 是不完整类型的优势是什么?
    • 正是 R.M.F.,我的问题可以改写为:为什么 应该 void 是一个不完整的类型?
    • @leftaroundabout:如果 C++ 是一种真正的函数式语言,我们将只使用tuple&lt;&gt;(一个有 0 个成员的元组)。但从历史上看,C++ 是 C 的扩展。这句话在 Java 中会更有意义,但即使他们选择了 C 的模型(“java.lang.Void 是一个不可实例化的占位符类”)
    【解决方案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*,那么语义会变得很奇怪。您有一个指向 typed 内存的指针。目前,C++ 将不相关的类型化指针(某些特殊情况除外)之间的转换定义为未定义的行为。现在标准必须为moo 类型添加另一个例外。它必须说明当你这样做时会发生什么:

    moo *m = new moo;
    *moo;
    

    对于void,这是一个编译器错误。 moo 是怎么回事?还是没用没意义,但编译器必须允许。

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

    【讨论】:

    • 在很多情况下,您希望将多态类型的参数特化为空,因为这样做在逻辑上是有意义的,使代码更具可读性,提高了代码重用等。我并不感到惊讶不能从 void 转换为 moo,但这在实践中永远不会出现。
    • 添加一些额外的奖励:throw moo()moo[5] - 所有不适用于 void 的东西因为不完整。
    • @JeffBurdges:然后您可以创建自己的空“无”类并做您需要的事情。
    • 我之所以问,是因为单元(无)类型在具有比 C++ 更仔细考虑类型系统的语言中是基本的,令人信服的答案是“除了历史上的便利性之外没有充分的理由C",这很有道理。 IE。人们实际上担心转换为void,因为他们一开始几乎没有使用类型安全。 void 在类型安全的世界中可能没有任何意义。
    • @JeffBurdges:也许对你很有说服力;不一定对别人。你没有提供一个令人信服的理由(对我来说)实际上在语言中内置了这样一个结构。
    猜你喜欢
    • 2012-03-11
    • 2018-04-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-20
    • 2012-07-09
    • 2018-03-16
    相关资源
    最近更新 更多