隐式转换在现代C++是好是坏?

Are implicit conversions good or bad in modern C++?

本文关键字:C++ 转换      更新时间:2023-10-16

在此提案中:

N3830 作用域内资源 - 标准库的通用 RAII 包装器

提出了一个scoped_resource的 RAII 包装器。

在第 4 页上,有一些这样的代码:

auto hFile = std::make_scoped_resource(
...
);    
...
// cast operator makes it seamless to use with other APIs needing a HANDLE
ReadFile(hFile, ...);

Win32 APIReadFile()采用原始HANDLE参数,而不是hFilescoped_resource的实例,因此为了使上述代码正常工作,有一个由scoped_resource实现的隐式强制转换运算符。

但是,"现代"建议不是为了避免这种隐式转换吗?

例如,ATL/MFCCString有一个隐式转换(强制转换运算符)到LPCTSTR(const char/wchar_t*,即原始 C 字符串指针),而不是 STL 字符串具有显式c_str()方法。

类似地,像unique_ptr这样的智能指针有一个显式get()方法来访问底层包装的指针;反对隐式转换的建议似乎也出现在这篇博文中:

读者问答:为什么现代智能指针不隐式转换为*?

那么,这些隐式转换(如ATL/MFCCString和新提出的scoped_resource)是否适用于现代C++?

从编码的角度来看,我会说能够简单地将 RAII 包装器(无论是CString还是scoped_resource)传递给期望"原始"参数(如原始 C 字符串指针或原始句柄)的 C API,依靠隐式转换,而无需调用一些.GetString()/.get()方法,似乎非常方便。

以下是引自C++入门第 5 版。

注意:避免过度使用转换函数

与使用重载运算符一样,明智地使用转换运算符可以 大大简化了类设计人员的工作,并使类的使用更加容易。 但是,某些转换可能会产生误导。转换运算符是 当类类型之间没有明显的单一映射时具有误导性 和转换类型。

例如,考虑一个表示日期的类。我们可能会认为 提供从Dateint的转换是个好主意。然而 转换函数应返回什么值?该函数可能会返回 年、月和日的十进制表示形式。例如,1989 年 7 月 30 日可能表示为 int 值19800730。或者,转换运算符可能会返回一个 int,表示自某个纪元点(如 1970 年 1 月 1 日)以来经过的天数。这两种转换都具有理想的属性,即以后的日期对应于较大的整数,因此任何一种都可能有用。

问题是 对象类型为Date,值类型为int。在这种情况下,最好不要 以定义转换运算符。相反,类应该定义一个或 更多的普通成员以这些不同的形式提取信息。

所以,我可以说的是,在实践中,类应该很少提供转换运算符。很多时候,如果转换自动发生,用户更有可能感到惊讶,而不是得到 转换的存在。但是,此规则有一个重要的例外 thumb:类定义转换为bool的情况并不少见。 在标准的早期版本中,想要定义转换为的类 bool 面临一个问题:因为 bool 是一个算术类型,一个类类型的对象 转换为 bool 可用于需要算术类型的任何上下文。 这种转换可能会以令人惊讶的方式发生。特别是,如果istream有一个 转换为bool,以下代码将编译:

int i = 42;
cin << i; // this code would be legal if the conversion to bool were not explicit!

应使用explicit转换运算符。下面是一个小例子:

class small_int 
{
private: 
int val;
public:
// constructors and other members
explicit operator int() const { return this->val; }
}

。并在程序中:

int main()
{
SmallInt si = 3; // ok: the SmallInt constructor is not explicit
si + 3; // error: implicit is conversion required, but operator int is explicit
static_cast<int>(si) + 3; // ok: explicitly request the conversion
return 0;
}

我认为隐式转换对应用程序程序员来说是好的/方便的,但对库开发人员来说是坏的,因为它们引入了复杂性和其他问题: 一个很好的例子是隐式转换和模板推导之间的相互作用。

从库的角度来看,将语言功能限制为最小集更容易。为此,您甚至可以说,如果删除一些重载和默认参数,库更容易维护。对于应用程序程序员来说不太方便,但歧义和复杂性较小。

因此,这实际上是在方便和简单之间做出的选择。

它在很大程度上取决于确切的转换类型,但一般来说,在某些领域,隐式转换可能会带来问题,而显式转换可能不会。

通常,在编程中显式执行任何操作都是一种更安全的方法。

关于一般C++,特别是隐式和显式转换,有一个很好的信息来源。