C++ 从派生类到基类的自动转换';成员变量';s型

C++ 从派生类到基类的自动转换';成员变量';s型,c++,type-conversion,implicit-conversion,derived-class,std-pair,C++,Type Conversion,Implicit Conversion,Derived Class,Std Pair,长话短说:我想理解为什么在下面代码的最后一行中没有使用d::operator B()const转换运算符,因此在使用g++-std=c++17 source.cpp进行编译时失败(尽管使用g++-std=c++2a deleteme.cpp进行编译是成功的) 错误是: $g++-std=c++17 deleteme.cpp&&./a.out 在/usr/include/c++/10.2.0/cassert:44中包含的文件中, 来自deleteme.cpp:1: deleteme.cpp:在函数

长话短说:我想理解为什么在下面代码的最后一行中没有使用
d::operator B()const
转换运算符,因此在使用
g++-std=c++17 source.cpp进行编译时失败(尽管使用
g++-std=c++2a deleteme.cpp进行编译是成功的)

错误是:

$g++-std=c++17 deleteme.cpp&&./a.out
在/usr/include/c++/10.2.0/cassert:44中包含的文件中,
来自deleteme.cpp:1:
deleteme.cpp:在函数“int main()”中:
deleteme.cpp:19:14:错误:与“operator==”不匹配(操作数类型为“D”和“B”)
19 |断言(d==B{2});//未显式调用转换运算符错误//行
|            ~ ^~ ~~~~
|            |    |
|D B
在/usr/include/c++/10.2.0/utility:70中包含的文件中,
来自deleteme.cpp:2:
/usr/include/c++/10.2.0/bits/stl_pair.h:466:5:注:候选:“模板constexpr bool std::operator==(const std::pair&,const std::pair&)”
466 |运算符==(常数对和常数x,常数对和常数y)
|     ^~~~~~~~
/usr/include/c++/10.2.0/bits/stl_pair.h:466:5:注意:模板参数推导/替换失败:
在/usr/include/c++/10.2.0/cassert:44中包含的文件中,
来自deleteme.cpp:1:
deleteme.cpp:19:20:注意:“B”不是从“const std::pair”派生的
19 |断言(d==B{2});//未显式调用转换运算符错误//行
|       
代码是:

#包括
#包括
结构B{
int x;
B(intx):x(x){}
布尔运算符==(B const&other)const{return x==other.x;}
};
结构D:std::pair{
运算符B()常量{返回this->first;}
};
int main(){
B{1};
D{std::pair(B{2},(char*)“hello”)};
assert((B)d==B{2});//显式调用的转换运算符可以
assert(d==B{2});//未显式调用转换运算符错误//行
}
这是一个后续问题。在那里,我得到了帮助,编写了一个类
Recursive
,其行为类似于一对(因此继承自它),其
第一个
std::array
,第二个
boost::hana::optional
(有关详细信息,请参阅链接)


由于
std::pair
second
first
中包含的一种“高级”信息,因此在许多地方,当编译器看到
d==B{2}
时,我想将
std::pair
类的
递归的
对象转换为它的
first
类型,它首先需要能够找到,然后对它们执行重载解析

如链接所述,重载列表包含:

  • 第一个操作数(如果有)的成员
    运算符==
    s
  • 非成员
    运算符==
    s通过非限定查找(如果有)找到
  • 内置
    运算符==
    s,如果操作数可以转换为内置类型
没有提到检查第一个操作数的转换运算符,因此找不到
运算符==

解决方案是使
操作符==
成为非成员(可能将其定义为
朋友
)。这项工作:

friend bool operator==(const B &a, const B &b) {return a.x == b.x;}

从C++20开始,比较运算符的规则放松了:编译器也将在第二个操作数中查找成员
运算符==
,并将愉快地使用错误的参数顺序调用非成员
运算符==

因此
a==b
b==a
现在等同于一个学位,除了:

  • 以这种新方式调用的
    运算符==
    必须返回
    bool
  • 如果给出选择,编译器将更喜欢调用
    操作符==
    的旧方法,而不是调用rhs的成员
    操作符==
    ,这反过来又比调用参数顺序错误的非成员
    操作符==
    更为可取

@Holy Why d应该转换为B而不是B{2}转换为d?@mfnx,因为我可以切掉
d
B
不同的地方,利用它有一个
B
和其他东西的事实(例如,
char*
,我稍微修改了代码)。将
B
转换为
D
将意味着“发明”一种方法来填充
第二个
元素,因为我提供了一个从
D
转换为
B
的函数,并且没有从
B
转换为
D
的函数,所以我的选择似乎相当简单。为什么C++17编译器如此不情愿?你的直觉是合理的。答案是正确的,但最后一句话“…参数顺序错误”有点不对劲<代码>=
应该始终是对称的,即使作为成员,也应该是对称的,所以没有错误的顺序。c++20刚刚修复了这个漏洞。所以“错误顺序”应该是“任意顺序”:@cigien发现它们并不完全相同,所以在我的编辑中,我仍然需要区分原始顺序和反向顺序。我本可以称之为“反向”,但呃:P