为什么在第一顺序中将{} 转换为std::nullptr_t?

19

这段代码:

#include <iostream>
#include <vector>

using namespace std;

void dump(const std::string& s) {
    cout << s << endl;
}

class T {
public:
    T() {
        dump("default ctor");
    }

    T(std::nullptr_t) {
        dump("ctor from nullptr_t");
    }

    T(const T&) {
        dump("copy ctor");
    }

    T& operator=(const T&) {
        dump("copy operator=");
        return *this;
    }

    T& operator=(std::nullptr_t) {
        dump("operator=(std::nullptr_t)");
        return *this;
    }

    T& operator=(const std::vector<int>&) {
        dump("operator=(vector)");
        return *this;
    }
};

int main() {
    T t0;

    t0 = {};

    return 0;
}

输出结果: 输出
default ctor
operator=(std::nullptr_t)

为什么选择使用 std::nullptr_t 作为 operator= 的参数?


5
我认为从{}std::nullptr_t是标准转换(实际上是恒等转换,[over.ics.list]),但其他转换是用户定义的,因为它们导致用户定义的类型。 - Kerrek SB
1
通常当我看到类型 T 时,它是一个模板参数。这让我有点困惑。 - Mark Ransom
1个回答

19
我们有三个候选者:
  1. operator=(T const& )
  2. operator=(std::vector<int> const& )
  3. operator=(std::nullptr_t )
对于#1和#2,{}会导致用户定义的转换序列
然而,对于#3,{}标准转换序列,因为nullptr_t不是一个类类型。
由于标准转换序列优于用户定义的转换序列,所以#3获胜。

1
所以,如果你不想让 std::nullptr_t 这样做,你可以编写自己的类型,该类型可以从 std::nullptr_t 隐式转换,这将使所有三个选项成为用户定义的转换序列,因此它将是一个模糊的重载。 - Justin
1
@Justin,它实际上似乎很有用,因为{}空无一物,而nullptr_t通常意味着什么都没有。 - Yakk - Adam Nevraumont
1
@MarkRansom 没有鸡生蛋的情况。重载解析从名称查找开始,然后我们检查所有候选项的可行性并修剪那些不可行的,然后我们选择哪一个是最佳匹配-最佳匹配的参数类型是实际初始化的类型。你觉得还有什么不清楚的吗? - Barry
编译器在参数类型未知时如何选择最佳匹配?这就是我的问题,{}本身没有类型。或者如果它实际上有类型,我想知道它是如何推导出来的。 - Mark Ransom
1
@vladon 这个... 不太对。std::initializer_list 不是一个类型,而是一个类模板。{} 是一个“初始化列表”(术语),但它不是任何 Tstd::initializer_list<T> 实例。它没有类型。 - Barry
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接