返回信息流最近在学`class`与类型转换,发现书上讲的都是自定义类与内置类型的互相转换,没介绍自定义类型间的转换。于是我就自己写点代码研究一下。
首先我写了这段代码:
```C++
#include<iostream>
using namespace std;
class B;
class A{
public:
A(int m){
n=m;
}
operator B();
friend B;
private:
int n;
};
class B{
public:
B(){}
B(A a){
cout<<"B::B(A) called."<<endl;
n=a.n;
}
friend A;
private:
int n;
};
A::operator B(){
cout<<"A::operator B() called."<<endl;
B tmp;
tmp.n=n;
return tmp;
}
int main(){
(B)(A)0;
return 0;
}
```
运行结果是`B::B(A) called.`。我再把`B::B(A)`删去,就得到运行结果`A::operator B() called.`。
这个实验说明,`B::B(A)`和`A::operator B()`都能实现显式类型转换,且前者的优先级比后者高。
接下来我把主函数改一下,改成
```C++
int main(){
(B)0;
return 0;
}
```
我对这个代码的期望是,**`int(0)`通过隐式类型转换变为`A(0)`,然后`A(0)`通过显式类型转换变为`B(A(0))`**。
再重复以上实验,结论是
> 当存在`B::B(A)`时(无论有无`A::operator B()`),这个类型转换过程可以完成;
否则(仅存在`A::operator B()`时),这个类型转换过程不可以完成(报错信息:`[Error] no matching function for call to 'B::B(int)'`)。
看来`B::B(A)`和`A::operator B()`所实现的类型转换在某些地方是有差异的。这引发了我极大的兴趣,所以我打算再深入研究一下。
我写了[四段代码(1~4)](https://txtpad.cn/cppHusky_public),希望分别用四种方法来实现从`A`到`B`的隐式类型转换和从`B`到`C`的显式类型转换。下面我用表格来呈现这组实验的结果:
|过程|代码1|代码2|代码3|代码4|
|---|---|---|---|---|
|`A`->`B`|`B::B(A)`|`A::operator B()`|`B::B(A)`|`A::operator B()`|
|`B`->`C`|`C::C(B)`|`C::C(B)`|`B::operator C()`|`B::operator C()`|
|是否可行|*Yes*|*Yes*|*No*|*No*|
从结果来看,貌似`B`到`C`的转换方式对整个过程有决定性作用,这里**用`C::C(B)`能完成,而用`B::operator C()`不能完成**。
但真的是这样吗?如果转换过程环节更长,这个结论是否也成立呢?
为了进一步验证我的结论,我又写了[八段代码(5~12)](https://txtpad.cn/cppHusky_public),希望实现从`A`到`B`的隐式类型转换、从`B`到`C`的隐式类型转换和从`C`到`D`的显式类型转换。下面我用表格来呈现这组实验的结果:
|过程|代码5|代码6|代码7|代码8|
|---|---|---|---|---|
|`A`->`B`|`B::B(A)`|`A::operator B()`|`B::B(A)`|`A::operator B()`|
|`B`->`C`|`C::C(B)`|`C::C(B)`|`B::operator C()`|`B::operator C()`|
|`C`->`D`|`D::D(C)`|`D::D(C)`|`D::D(C)`|`D::D(C)`|
|是否可行|*No*|*No*|*No*|*No*|
|**过程**|**代码9**|**代码10**|**代码11**|**代码12**|
|`A`->`B`|`B::B(A)`|`A::operator B()`|`B::B(A)`|`A::operator B()`|
|`B`->`C`|`C::C(B)`|`C::C(B)`|`B::operator C()`|`B::operator C()`|
|`C`->`D`|`C::operator D()`|`C::operator D()`|`C::operator D()`|`C::operator D()`|
|是否可行|*No*|*No*|*No*|*No*|
情况变得出乎我的意料。**无论用哪种转换组合,都不能实现我的预期**。(报错信息:`[Error] no matching function for call to 'D::D(A)'`)
那这又是因为什么呢?
我又回去思考了一下代码1和代码5的区别。代码5比代码1唯一多出的无非就是多了一次**连续的**隐式类型转换,难道说是连续隐式类型转换次数的问题?(代码1进行了1次连续的隐式类型转换和1次显式类型转换,而代码5进行了2次连续的隐式类型转换和1次显式类型转换)
为了搞清楚这个问题,我又写了这样一段代码:
```C++
#include<iostream>
using namespace std;
class B;
class A{
public:
A(double m){
n=m;
}
friend B;
private:
double n;
};
class B{
public:
B(A a){
n=a.n;
}
private:
double n;
};
int main(){
(B)'0';
return 0;
}
```
在`(B)'0';`中,`'0'`先隐式类型转换成`48.0`,然后隐式类型转换成`A(48.0)`,最后显式类型转换成'B(A(48.0))`。
如果两次连续隐式类型转换不被允许,那么这段代码不应当通过编译;但它完美地通过了编译。
难道说涉及内置类型的连续隐式类型转换与涉及自定义类型的连续隐式类型转换有所不同?还是说另有隐情?
(研究一上午快晕厥了,望各位大神解答)
这是一条镜像帖。来源:北邮人论坛 / cpp / #102863同步于 2023/8/23
该镜像源已超过 30 天没有更新,可能在源站已被删除。
CPP机器人发帖
【问题】【讨论】关于自定义类型的隐式类型转换
cppHusky
2023/8/23镜像同步5 回复
订阅后,新回复会通过你的通知中心匿名送达。
5 条回复
主楼说得比较乱,这里再把几个关键问题整理一下:
- 在涉及类型转换的场合下,`B::B(A)`和`A::operator B()`有何区别(以`A`到`B`为例)?
- 在涉及连续类型转换的场合下,隐式类型转换能否连续进行?
- 在涉及连续类型转换的场合下,内置类型的表现与自定义类型不相同吗?
借楼问下暖神 C++ 的 ABI spec 在哪里看呢?我下载了 C++17 spec,并没有找到相关内容。比如下面这个例子:
``` c++
#include <iostream>
class EmptyFoo {
};
class Foo {
public:
int c;
int d;
char name[64];
Foo(EmptyFoo empty_foo) {
a = 0;
b = 0;
}
Foo(int a, int b) {
this->a = a;
this->b = b;
}
private:
int a;
int b;
};
void show(Foo foo) {
std::cout << "foo.c: " << foo.c << ", foo.d: " << foo.d << std::endl;
}
int main()
{
EmptyFoo empty_foo;
Foo foo(empty_foo);
std::cout << "sizeof(EmptyFoo) = " << sizeof(EmptyFoo) << std::endl;
show(foo);
return 0;
}
```
对于 show() 函数,用 GCC 13 编译之后,编译器只会把变量 foo 的 c 和 d 两个成员变量的值传给 show() 函数,而 GCC 4.8 里则是会复制整个类对象,然后传进去。
【 在 nuanyangyang 的大作中提到: 】
: 练习题:请引用C++20 specification解释上述行为。
ABI和平台相关。比如X86的linux的abi,找System V ABI。这里有一些链接:份:https://wiki.osdev.org/System_V_ABI
ARM的ABI可以找ARM官网AAPCS,GitHub上也有:https://github.com/ARM-software/abi-aa 比如你可以找找AAPCS64。
RISC-V的spec里定义了一个标准的calling convention。看https://github.com/riscv/riscv-isa-manual/releases/download/Ratified-IMAFDQC/riscv-spec-20191213.pdf 里面的第25章
【 在 nitroethane 的大作中提到: 】
: 借楼问下暖神 C++ 的 ABI spec 在哪里看呢?我下载了 C++17 spec,并没有找到相关内容。比如下面这个例子:
: [md]
: ``` c++
: ...................