如果声明了对类型的转换运算符和对类型的引用,为什么直接列表初始化会导致类型引用转换的歧义?
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 int
或 operator int&
.
生成 int
但即使它没有歧义,您仍然会得到一个对纯右值的非常量左值引用。这是非法的。所以这段代码永远不会起作用。
Braced-init-lists(大括号)初始化 objects,而不是 references 对象。如果您已经有一个对象并想获得对它的引用,请不要使用 braced-init-lists。
But in this case why does compiler accept #5?
因为列表初始化是一系列有优先级的规则。一个比我上面指出的规则具有更高优先级的规则是包含单个值的 braced-init-list 的情况,其类型与正在初始化的类型相同。 #5 和 6 正好符合这个要求,因为 d
、e
和 a
都是 int&
。
但是,如果您只是听取我的建议,而不是在不尝试创建对象时使用大括号初始化列表,那么您就不必担心这样的极端情况。
问题出现在
考虑一个例子:
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 int
或 operator int&
.
int
但即使它没有歧义,您仍然会得到一个对纯右值的非常量左值引用。这是非法的。所以这段代码永远不会起作用。
Braced-init-lists(大括号)初始化 objects,而不是 references 对象。如果您已经有一个对象并想获得对它的引用,请不要使用 braced-init-lists。
But in this case why does compiler accept #5?
因为列表初始化是一系列有优先级的规则。一个比我上面指出的规则具有更高优先级的规则是包含单个值的 braced-init-list 的情况,其类型与正在初始化的类型相同。 #5 和 6 正好符合这个要求,因为 d
、e
和 a
都是 int&
。
但是,如果您只是听取我的建议,而不是在不尝试创建对象时使用大括号初始化列表,那么您就不必担心这样的极端情况。