为什么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 *
如下:
ss << u
return 引用 ss
。
ss
隐式转换为 void *
如果流操作失败则为 nullptr
,如果流仍然有效则为非空。
&&
运算符会快速中止,具体取决于该指针的真值。
- 第二部分
ss >> t
运行,并且returns也转换为void *
。
- 如果两个操作都成功,那么我们可以 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(或更高版本)兼容的编译器。
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 *
如下:
ss << u
return 引用ss
。ss
隐式转换为void *
如果流操作失败则为nullptr
,如果流仍然有效则为非空。&&
运算符会快速中止,具体取决于该指针的真值。- 第二部分
ss >> t
运行,并且returns也转换为void *
。 - 如果两个操作都成功,那么我们可以 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(或更高版本)兼容的编译器。