了解最新技术文章
有时您想向类型添加隐式转换。这可以通过添加隐式转换运算符来完成。例如,std::string
可隐式转换为std::string_view
:
这种转换是安全的、廉价的,并且std::string
和std::string_view
代表相同的柏拉图式值——我们符合Tony van Eerd 的隐式转换标准,并且使用隐式转换是合理的。
在 think cell,我们目前正在更改字符串文字,以便它们不再具有 type char const[N]
,而是自定义类型(将在以后的帖子中详细介绍)。我们string_literal
暂时就这么称呼吧。为了向后兼容和方便,我们希望能够string_literal
用作当前采用char const*
. 因此我们添加一个隐式转换:
然而,与std::string
的转换运算符不同,这不是一个好主意,因为我们返回内置的 a 类型,并且转换可以链接在所谓的用户定义的转换序列中。
用户定义的转换序列由初始标准转换序列、用户定义的转换 ([class.conv]) 和第二个标准转换序列组成。
标准转换序列是按以下顺序进行的标准转换序列:
以下集合中的零次或一次转换:左值到右值转换、数组到指针转换以及函数到指针转换。
来自以下集合的零次或一次转换:整型提升、浮点提升、整型转换、浮点转换、浮点整型转换、指针转换、指针到成员转换和布尔转换。
零或一个函数指针转换。
零或一次资格转换。
第二个标准转换序列尤其可能存在问题,因为它适用于转换运算符的结果:
这通常是不受欢迎的——我们不希望我们的字符串类型本身像指针一样,我们只是希望它在初始化指针参数时能够隐式转换为一个。
幸运的是,我们可以通过(讽刺地)模板化转换运算符并约束模板参数来修复它并防止第二个标准转换序列:
Nowstring_literal
可以隐式转换为任何类型T
,只要该类型是char const*
。与之前的版本有什么区别?重载解析不会考虑第二个标准转换序列,因为它可以直接插入最终目标类型。如果该类型不是char const*
,我们将出现替换失败:
因此,我提出以下指导方针:
将隐式转换运算符写入类型时foo
,将其编写为模板并将类型限制为与以下内容相同foo
:
template <std::same_as<foo> T>operator T() const noexcept;
这样,您就可以防止用户定义的转换序列中出现其他隐式转换。foo
如果是内置类型,这一点尤其重要。
一个缺点是转换运算符现在是一个模板,尽管它只返回单一类型。因此,如果隐式转换运算符的主体中有很长的定义,则必须将其移至标头。但为什么你首先要定义复杂的隐式转换呢?!
您可能想简化转换运算符的定义:
但是,这不是模板:它是一个非模板函数,具有推导的返回类型,仅限于对概念进行建模std::same_as<foo>
。因此,您的行为与operator foo()
!
24小时免费咨询
请输入您的联系电话,座机请加区号