如果声明了对类型的转换运算符和对类型的引用,为什么直接列表初始化会导致类型引用转换的歧义?

Why does direct list initialization causes ambiguity for type reference cast if cast operators to the type and reference to the type are declared?

问题出现在 的上下文中。

考虑一个例子:

struct foo {
    int value;
    operator int&(){ return value; }
    operator int(){ return value; }
};

int main () {
    int &a(foo{}); // #1
    //int &b{foo{}}; // #2 -- ambiguity
    int &c = foo{}; // #3
    //int &d = {foo{}}; // #4-- ambiguity
    int &d { a }; // #5
    int &e = { a }; // #6
    (void)a;
    (void)c;
    (void)d;
    (void)e;
}

我不明白为什么#2 和#4 会导致歧义,而#1 和#3 不会。所以问题是 - 如果声明了对类型的转换运算符和对类型的引用,为什么直接列表初始化会导致隐式转换引用的歧义?

列表初始化,当用于初始化引用时,会将列出的值转换为纯右值(又名:临时值),用于直接初始化引用。

所以 int &b{foo{}} 在功能上等同于 int &b(int(foo{}))。这是模棱两可的;它可以通过 operator intoperator int&.

生成 int

但即使它没有歧义,您仍然会得到一个对纯右值的非常量左值引用。这是非法的。所以这段代码永远不会起作用。

Braced-init-lists(大括号)初始化 objects,而不是 references 对象。如果您已经有一个对象并想获得对它的引用,请不要使用 braced-init-lists。


But in this case why does compiler accept #5?

因为列表初始化是一系列有优先级的规则。一个比我上面指出的规则具有更高优先级的规则是包含单个值的 braced-init-list 的情况,其类型与正在初始化的类型相同。 #5 和 6 正好符合这个要求,因为 dea 都是 int&

但是,如果您只是听取我的建议,而不是在不尝试创建对象时使用大括号初始化列表,那么您就不必担心这样的极端情况。