【发布时间】:2020-07-25 03:23:46
【问题描述】:
#include <iostream>
struct B;
struct A{
operator B&&() const;
};
struct B{
B(A const&){
}
B() {}
};
int main(){
A a;
B&& rf = a; //#1
}
B g;
A::operator B&&() const {
std::cout<<"execute\n";
return std::move(g);
}
考虑上面的代码,结果是here。 #1 的引用绑定遵循以下规则:
否则,引用应为对非易失性 const 类型的左值引用(即 cv1 应为 const),或者引用应为右值引用。
- 如果初始化表达式
- 是右值(但不是位域)或函数左值,“cv1 T1”与“cv2 T2”引用兼容,或者
- 具有类类型(即T2是类类型),其中T1与T2没有引用相关,可以转换为“cv3 T3”类型的右值或函数左值,其中“cv1 T1”是与“cv3 T3”参考兼容(参见[over.match.ref]),
那么在第一种情况下的初始化表达式的值和在第二种情况下的转换结果称为转换后的初始化程序。如果转换后的初始值设定项是纯右值,则将其类型 T4 调整为类型“cv1 T4”([conv.qual])并应用临时实现转换。在任何情况下,引用都绑定到生成的 glvalue(或适当的基类子对象)。
- 否则:
- 如果 T1 或 T2 是类类型,并且 T1 与 T2 没有引用相关,则使用通过用户定义的转换([ dcl.init]、[over.match.copy]、[over.match.conv]);如果相应的非参考复制初始化格式错误,则程序格式错误。调用转换函数的结果,如针对非引用复制初始化所描述的,然后用于直接初始化引用。对于这种直接初始化,不考虑用户定义的转换。
根据上述规则的结构,If 分支的第二个子弹对于B&& rf = a; 就足够了,因此A 类的operator B&&() const 是唯一的候选转换函数,换句话说,只要满足if的情况,那么otherwise的分支就永远不会下架了。
GCC 的结果证明了这些规则所说的内容,但是Clang 抱怨执行引用绑定的转换函数不明确(Clang 似乎将各自分支中的两个转换函数都视为候选函数)。是clang的bug吗?
尽管如此,case
#include <iostream>
struct B;
struct A{
operator B&&() const;
};
struct B{
B(A&){
}
B() {}
};
int main(){
A a;
B&& rf = a; //#1
}
B g;
A::operator B&&() const {
std::cout<<"execute\n";
return std::move(g);
}
GCC 还是同意operator B&&() const 是执行引用绑定的唯一转换函数。
【问题讨论】:
-
评论不用于扩展讨论;这个对话是moved to chat。
标签: c++ c++17 language-lawyer