C++中的强制类型转换顺序

14

我想问一下C++中的类型转换问题。听说当类型转换存在歧义时编译器应该会报错,但是为了更好地理解,我进行了测试,结果没有报错,而且使用了相当奇怪的函数顺序。那么,在什么情况下会出现这种情况呢?

A foo;
B bar = foo;

它使用了类型转换运算符,但是当我输入:

bar = static_cast<B>(foo);

它使用了单个参数构造函数。

有人能解释一下为什么会出现这种情况吗?

我使用的整段代码:

#include <iostream>
#include <typeinfo>

using namespace std;

class B;

class A {
public:
    A() {}
    A (const B& x);
    A& operator= (const B& x);
    operator B();
};

class B {
public:
    B() {}
    B (const A& x) {
        cout << "constructor B" << endl;
    }
    B& operator= (const A& x) {
        cout << "Assign B" << endl;
        return *this;
    }
    operator A() {
        cout << "Outer B" << endl;
        return A();
    }
};

A::A (const B& x) {
    cout << "constructor A" << endl;
}
A& A::operator= (const B& x) {
    cout << "Assign A" << endl;
    return *this;
}
A::operator B() {
    cout << "Outer A" << endl;
    return B();
}

int main ()
{
    A foo;

// First one
    B bar = foo;

    bar = foo;
    foo = bar;

// Second one    
    bar = static_cast<B>(foo);

    B bar2 = static_cast<B>(foo);
    foo = static_cast<A>(bar);
    B bar3 = foo;
    A foo2 = bar3;
    A foo3 = B();
    foo3 = B();

    return 0;
}

编辑:

我的输出:

Outer A
Assign B
Assign A
Copy constructor B
Copy constructor B
Copy constructor A
Outer A
Outer B
Outer B
Assign A

只是为了完整起见:您能否将您的输出也添加到您的问题中? - Hayt
我只是没有删除这个注释,这是从http://www.cplusplus.com/doc/tutorial/typecasting/编辑的代码。 - Michał Ziobro
也许你应该添加你使用的编译器?我用GCC得到了不同的结果。看起来是未指定的。 - Hayt
标准特别将 C 风格的转换作为需要调用转换函数的示例。 直觉认为对于 static_cast 也应该如此。 - Bartek Banachewicz
1
static_cast 总是创建一个临时对象:https://dev59.com/1G025IYBdhLWcg3wAxF9 - drRobertz
显示剩余13条评论
1个回答

3
你的编译器没有抱怨二义性的原因是你的构造函数和赋值运算符采用了 const A/B&,但是 operator A()operator B() 没有声明为 const。对于非 const 对象的转换,编译器因此更喜欢使用 operator A/B()
我认为其余部分可以用 static_cast 转换规则 来解释,这在你的代码中相当于直接初始化的行为和重载决议(这就是为什么赋值运算符只在最后一个示例中被调用的原因)。

嗯,看起来你是对的。但为什么operator=也是const,却不能起到同样的作用呢? - Michał Ziobro
我不太确定你的意思是什么;你在示例中指的是哪一行? - acs
仅使用采用常量引用的operator=而不是operator B()bar = foo; - Michał Ziobro
在第一种情况下,编译器必须在转换构造函数和转换函数之间进行选择。在第二种情况下,它必须在operator=(const A&)operator=(const B&)之间进行选择,因此它使用不涉及转换的那个。 - Oktalist

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接