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

Why operator void*() conversion function added to the C++ stream classes?

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及以后的编译器中编译失败,因为去掉了这个转换函数。但问题是,如果它是危险的,为什么它是 C++ 标准库的一部分?允许将流对象转换为 void* 的目的是什么?它有什么用?

std::stringstream 的一个特点是,如果流被用作 bool,如果流仍然有效并且 false 如果不是。例如,如果您实现自己的词法转换版本,这可以让您使用简单的语法。

(作为参考,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 return 引用 ss
  2. ss 隐式转换为 void * 如果流操作失败则为 nullptr,如果流仍然有效则为非空。
  3. && 运算符会快速中止,具体取决于该指针的真值。
  4. 第二部分ss >> t运行,并且returns也转换为void *
  5. 如果两个操作都成功,那么我们可以 return 流式处理结果 t。如果其中任何一个失败,我们就会发出错误信号。

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 * 解决方法仍然存在问题。在 boost 中,为 C++11 之前的代码开发了更复杂的 "safe bool" 习语,iiuc 基于类似 T* 的东西,其中 T 是 "type-used once",例如在 class 中定义的结构实现了惯用语,或者使用指向成员函数的指针 class 并使用 return 值,这些值要么是那个的特定私有成员函数class,或 nullptr。 "safe bool" 习语用于所有 boost 智能指针的实例。

尽管在 C++11 中通过引入 explicit operator bool.

清理了整个混乱

更多信息在这里:Is the safe-bool idiom obsolete in C++11?

This is a flaw in the C++ standard library.

C++ 标准库旧版本(1998 和 2003)中的一个缺陷。该缺陷在 2011 及更高版本的标准中已不存在。语言中添加了一项新功能,即能够将转换运算符标记为 explicit,以使转换运算符对 bool 安全。

原始版本的 C++ 标准的开发人员明确选择使用到 void* 的转换而不是到 bool 的转换,因为到 bool 的非显式转换是在很多方面都很不安全。相比之下,虽然 operator void*() 显然很笨拙,但它确实有效,至少只要您没有将该指针指向其他对象或尝试删除它。 (另一种选择 operator! 可以说比任何一个转换运算符都更安全,但这需要非直观和深奥的 while (!!stream) {...}。)

"safe bool" 习语的概念是在标准的原始 1998/1999 版本发布后发展起来的。它是否在 2003 年之前开发有点无关紧要;该标准的 2003 版本旨在修复该原始标准的错误。 operator void*() let delete std::cin compile 与其说是错误不如说是 "don't do that then" 类问题。

"safe bool" 习语的发展表明确实存在使 operator bool() 安全的替代方案,但如果您查看任何实施,它们都非常复杂且非常笨拙。 C++11 的解决方案非常简单:允许使用 explicit 关键字限定转换运算符。 C++11 解决方案删除了​​ void* 转换运算符并添加了一个 explicit bool 转换运算符。这使得 "safe bool" 习语过时了,至少只要您使用的是 C++11(或更高版本)兼容的编译器。