为什么operator void *()转换函数被添加到C ++流类中?

时间:2015-08-23 16:38:57

标签: c++ c++11 delete-operator void-pointers c++-standard-library

C ++流类中有转换函数 operator void*() const 。这样所有流对象都可以隐式转换为void*。在与SO的程序员交互过程中,他们建议我不要使用void*,除非你有充分的理由使用它。 void*是一种消除类型安全的技术。错误检查。因此,由于该转换功能存在以下程序完全有效。这是C++ standard library.

中的一个缺陷
#include <iostream>
int main()
{
       delete std::cout;
       delete std::cin;
}

查看实时演示here.

上述程序在C ++ 03中有效,但在C ++ 11&amp;以后的编译器,因为这个转换函数被删除了。但问题是,如果它是危险的,它是C ++标准库的一部分?允许将流对象转换为void*的目的是什么?有什么用?

2 个答案:

答案 0 :(得分:25)

std::stringstream的一项功能是,如果该流用作bool,则如果该流仍然有效且true,则会转换为false如果不是。例如,如果您实现自己的lexical cast版本,则可以使用简单的语法。

(作为参考,boost包含一个名为lexical_cast的模板函数,它执行类似于以下简单模板函数的操作。)

template <typename T, typename U>
T lexical_cast(const U & u) {
    T t;
    std::stringstream ss;
    if (ss << u && ss >> t) {
        return t;
    } else {
        throw bad_lexical_cast();
    }
 }

例如,如果您在不允许例外的项目上工作,您可能希望改为使用以下版本。

template <typename T, typename U>
boost::optional<T> lexical_cast(const U & u) {
    T t;
    std::stringstream ss;
    if (ss << u && ss >> t) {
        return t;
    } else {
        return boost::none;
    }
 }

(上面有各种方法可以改进,这段代码只是举个例子。)

以上使用operator void *如下:

  1. ss << u会返回对ss
  2. 的引用
  3. ss隐式转换为void *,如果流操作失败则为nullptr,如果流仍然良好则为非空。
  4. &&运算符快速中止,具体取决于该指针的真值。
  5. 第二部分ss >> t运行,并返回并转换为void *
  6. 如果两个操作都成功,那么我们可以返回流式结果t。如果失败,那么我们发出错误信号。
  7. bool转换功能基本上只是语法糖,可以简洁地编写这个(以及许多其他内容)。

    实际引入隐式bool转换的缺点是,std::stringstream可隐式转换为int以及许多其他类型,因为bool可隐式转换为int }。这最终导致其他地方的语法噩梦。

    例如,如果std::stringstream进行了隐式operator bool转换,请假设您拥有以下简单代码:

    std::stringstream ss;
    int x = 5;
    ss << x;
    

    现在处于重载分辨率,你有两个潜在的重载要考虑(!),正常的一个operator<<(std::stringstream &, int),以及ss转换为bool的那个,然后提升为int,并且位移运算符可能会应用operator<<(int, int),因为隐式转换为bool ...

    解决方法是使用隐式转换为void *,可以在上下文中用作bool,但实际上不能隐式转换为bool或int。

    在C ++ 11中,我们不再需要这种解决方法,并且没有理由任何人明确使用void *转换,因此它只是被删除了。

    (我正在搜索引用,但我相信此函数的值仅指定为nullptr时的值,而不是nullptr时的值,并且它是实现定义了它可能产生的非零指针值。所以任何依赖于void *版本并且不能轻易地重构以使用bool版本的代码都是不正确的。)

    void *解决方法仍然没有问题。在提升中,为C ++之前的代码开发了一个更复杂的“安全bool”习惯,其中iiuc基于类似T*的内容,其中T是“类型使用过一次”,如在实现该习惯用法的类中定义的结构,或者使用该类的指向成员函数并使用返回值(该类是该类的特定私有成员函数或nullptr)。例如,在所有的boost智能指针中都使用了“安全bool”习语。

    无论如何通过引入explicit operator bool在C ++ 11中清理整个混乱。

    此处有更多信息:Is the safe-bool idiom obsolete in C++11?

答案 1 :(得分:11)

  

这是C ++标准库中的一个缺陷。

是C ++标准库旧版本(1998年和2003年)的一个缺陷。 2011年及以后版本的标准中不再存在此缺陷。我们在语言中添加了一项新功能,即将转化运算符标记为explicit,以使转化运算符安全bool

原始版本的C ++标准的开发人员明确选择使用转化为void*而不是转换为bool,因为非显式转换为bool非常不安全在很多方面。相比之下,虽然operator void*()显然很明显,但它起作用,至少只要你没有将指针强加给其他东西或试图删除它。 (替代方案operator!可以说比任何一个转换运算符更安全,但这需要非直观和深奥的while (!!stream) {...}。)

&#34;安全布尔的概念&#34; idiom是在原始的1998/1999版标准发布后开发的。它是否在2003年之前开发有点无关紧要;该标准的2003版本旨在成为该原始标准的错误修复。那个operator void*()delete std::cin编译并没有被视为一个错误,而不是那么做,然后才能做到这一点。&#34;有点问题。

&#34;安全布尔&#34;成语表明确实存在使operator bool()安全的替代方案,但是如果你看看任何一个实现,它们都是大规模的复杂和大规模的kludgy。 C ++ 11解决方案非常简单:允许转换运算符使用explicit关键字进行限定。 C ++ 11解决方案删除了​​void*转换运算符,并添加了explicit bool转换运算符。这使得&#34;安全的布尔&#34; idiom已经过时,至少只要您使用符合C ++ 11(或更高版本)的编译器。