问题出现在this answer的背景下。
考虑一个例子:
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则不然。所以问题是 - 为什么直接列表初始化会导致隐式强制转换引用歧义,如果声明了类型和类型引用的转换运算符?
答案 0 :(得分:7)
列表初始化,当用于初始化引用时,将获取列出的值并将它们转换为prvalue(又名:临时),它将用于指导初始化引用。
因此int &b{foo{}}
在功能上等同于int &b(int(foo{}))
。哪个含糊不清;它可以通过int
或operator int
生成operator int&
。
但即使它不含糊,你仍然会得到一个非常数左值的prvalue引用。哪个是非法的。所以这段代码从不开始工作。
Braced-init-lists(花括号)初始化对象,而不是引用到对象。如果您已有一个对象并希望获得它的引用,请不要使用braced-init-lists。
但在这种情况下,为什么编译器会接受#5?
因为列表初始化是一系列具有优先级的规则。优先级高于我上面指出的优先级的规则是包含单个值的braced-init-list,其类型与正在初始化的类型相同。 #5和6恰好适合该帐单,因为d
,e
和a
都是int&
s。
但是如果你只是接受我的建议而不是在你不尝试创建一个对象时使用braced-init-lists,那么你不必担心像这样的角落案例。