Порядок литья в С++

Я хотел бы спросить о кастинге на С++. Я слышал, что когда кастинг неоднозначен, компилятор должен вернуть ошибку, но, просто для лучшего понимания, я протестировал его, и он этого не сделал, более того, он использовал функции в довольно странном порядке. Когда:

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;
}

Edit:

Мой вывод:

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

Ответы

Ответ 1

Причина, по которой ваш компилятор не жалуется на двусмысленность, заключается в том, что ваши конструкторы и операторы присваивания принимают const A/B&, но operator A() и operator B() не объявляются const. Поэтому для преобразования неконстантных объектов компилятор предпочитает operator A/B().

Я думаю, что остальное можно объяснить правилами static_cast conversion, которые в вашем коде сводятся к поведению, как при прямой инициализации, и разрешение перегрузки (поэтому оператор присваивания вызывается только в последнем примере).