用户定义的隐式转换操作符和重载解析

User-defined implicit conversion operator and overload resolution

本文关键字:重载 操作符 转换 定义 用户      更新时间:2023-10-16

今天我在用户定义的隐式转换操作符中遇到了一个有趣的行为。

让我们看看这段代码:

struct Widget {
    Widget(uint64_t) {
    }
    Widget(const std::string &) {
    }
    operator uint64_t() {
        return static_cast<uint64_t>(123456789UL);
    }
    operator std::string() {
        return std::string("Hello");
    }
};

一个基本结构体,可以隐式转换为uint64_t或std::string。

现在,尝试通过std::cout:

打印出一个Widget实例
#include <iostream>
int main() {
    using std::cout;
    Widget w(123456);
    cout << w;
}

不管什么原因,Widget总是会被转换成uint64_t类型。首先,我希望调用是有歧义的,并通过标准的显式转换来限定为compile:

int main() {
    using std::cout;
    Widget w(123456);
    cout << static_cast<uint64_t>(w);

}

但由于某种原因,我现在忽略了,操作符uint64_t被选中。我试着去看c++规范,但找不到任何有用的东西来回答我的问题。

谁能帮我弄清楚编译器在做什么重载解析?

优先选择uint64_t转换。原因很简单,<<被重载为strings (basic_strings)的模板。在重载解析上,编译器总是倾向于精确匹配而不是模板。

当然你可以在c++规范中找到它。

读取 §13.3.1/2§13.3.1/3中的过载解析

重载解析以七种不同的方式选择要调用的函数语言中的上下文…

,

  • 首先,是候选函数的一个子集(具有适当数量的参数并满足某些其他条件的函数)(13.3.2)。
  • 然后根据每个参数与的匹配所需的隐式转换序列(13.3.3.1)选择最佳可行函数

,

简而言之,编译器首先列出要使用的候选对象,然后尝试找到最合适和更有效的重载。