技术文章

了解最新技术文章

当前位置:首页>技术文章>技术文章
全部 146 常见问题 7 技术文章 139

think cell博客:限制用户定义的转化

时间:2023-11-13   访问量:1039  标签: think cell,think-cell,转换序列

有时您想向类型添加隐式转换。这可以通过添加隐式转换运算符来完成。例如,std::string可隐式转换为std::string_view

class string { // template omitted for simplicitypublic:
    operator std::string_view() const noexcept {
       return std::string_view(c_str(), size());
    }};
std::string's 转换运算符的简化定义

这种转换是安全的、廉价的,并且std::stringstd::string_view代表相同的柏拉图式值——我们符合Tony van Eerd 的隐式转换标准,并且使用隐式转换是合理的。

think cell,我们目前正在更改字符串文字,以便它们不再具有 type char const[N],而是自定义类型(将在以后的帖子中详细介绍)。我们string_literal暂时就这么称呼吧。为了向后兼容和方便,我们希望能够string_literal用作当前采用char const*因此我们添加一个隐式转换:

class string_literal {public:
    operator const char*() const noexcept {
        return m_ptr;
    }};
的初始定义string_literal

然而,与std::string的转换运算符不同,这不是一个好主意,因为我们返回内置的 a 类型,并且转换可以链接在所谓的用户定义的转换序列中。

用户定义的转换序列由初始标准转换序列、用户定义的转换 ([class.conv]) 和第二个标准转换序列组成。

[over.ics.user]/1

标准转换序列是按以下顺序进行的标准转换序列:

  • 以下集合中的零次或一次转换:左值到右值转换、数组到指针转换以及函数到指针转换。

  • 来自以下集合的零次或一次转换:整型提升、浮点提升、整型转换、浮点转换、浮点整型转换、指针转换、指针到成员转换和布尔转换。

  • 零或一个函数指针转换。

  • 零或一次资格转换。


[一般转换]/1

第二个标准转换序列尤其可能存在问题,因为它适用于转换运算符的结果:

string_literal str = …;if (str) { // convert pointer to bool
    …}str + 1; // convert to pointer, then do arithmetic
不需要的隐式转换

这通常是不受欢迎的——我们不希望我们的字符串类型本身像指针一样,我们只是希望它在初始化指针参数时能够隐式转换为一个。

幸运的是,我们可以通过(讽刺地)模板化转换运算符并约束模板参数来修复它并防止第二个标准转换序列:

class string_literal {public:
    template <std::same_as<char const*> T>
    operator T() const noexcept {
        return m_ptr;
    }};
固定定义string_literal

Nowstring_literal可以隐式转换为任何类型T,只要该类型是char const*与之前的版本有什么区别?重载解析不会考虑第二个标准转换序列,因为它可以直接插入最终目标类型。如果该类型不是char const*,我们将出现替换失败:

string_literal str = …;if (str) { // error: no conversion from `string_literal` to `bool`
    …}str + 1; // error: no match for `operator+`
没有不需要的隐式转换

因此,我提出以下指导方针:

将隐式转换运算符写入类型时foo,将其编写为模板并将类型限制为与以下内容相同foo

template <std::same_as<foo> T>operator T() const noexcept;

这样,您就可以防止用户定义的转换序列中出现其他隐式转换。foo如果是内置类型,这一点尤其重要。

一个缺点是转换运算符现在是一个模板,尽管它只返回单一类型。因此,如果隐式转换运算符的主体中有很长的定义,则必须将其移至标头。但为什么你首先要定义复杂的隐式转换呢?!

您可能想简化转换运算符的定义:

operator std::same_as<foo> auto() const noexcept;
约束转换运算符的错误定义

但是,这不是模板:它是一个非模板函数,具有推导的返回类型,仅限于对概念进行建模std::same_as<foo>因此,您的行为与operator foo()!


上一篇:think cell博客:事件或无事件?

下一篇:think cell博客:关于char8_t

发表评论:

评论记录:

未查询到任何数据!

免费通话

24小时免费咨询

请输入您的联系电话,座机请加区号

免费通话

微信扫一扫

微信联系
返回顶部