Почему конструктор копирования называется - PullRequest
0 голосов
/ 24 января 2019

Я не могу понять, почему конструктор копирования вызывается вместо ошибки компиляции.

Я объявил два класса AB, которые являются независимыми.A не основан / не получен из B. Единственная связь между ними состоит в том, что в BI используется оператор, который преобразует B в AI, определил оператор =, который получает в качестве аргумента константную ссылку на B. В основном я написал следующий экземплярB = экземпляр A. Я ожидаю, что будет сгенерирована ошибка компиляции.Но оператор = называется

class A {};

class B {
public:
    // conversion from A (constructor):
    B()
    {

        cout << "1." << endl;
    }
    B(const A& x)
    {
        cout << "4." << endl;
    }
    // conversion from A (assignment):
    B& operator= (const B& x)
    {
        cout << "3." << endl;
        return *this;
    }
    // conversion to A (type-cast operator)
    operator A() {
        cout << "2." << endl;
        return A();
    }
};

int main()
{
    A foo;
    B bar;    // calls constructor
    bar = foo;      // calls assignment
                    //foo = bar;      // calls type-cast operator
    char c;
    c = getchar();
    return 0;
}

Я ожидаю ошибки компиляции.Но следующая последовательность печатается 1 4 3.Я с трудом понимаю, как вызывается конструктор копирования и почему оператор = не создает проблему

Спасибо

Ответы [ 3 ]

0 голосов
/ 24 января 2019

У вас есть неявный конструктор

B::B(const A&)

, который выполняет преобразование, которое вы не хотите. Вы можете изменить подпись на

explicit B(const A&);

, чтобы вызвать ошибку компиляции. Обратите внимание, что хорошей практикой является пометить конструкторы одним аргументом explicit по умолчанию (для этого тоже есть clang-tidy проверка ), просто чтобы избежать таких преобразований случайно (ИМХО, это было бы даже лучше, если конструкторы, которые могут быть построены с одним аргументом, будут по умолчанию explicit с возможностью сделать неявным).

0 голосов
/ 24 января 2019

Я ожидаю, что будет сгенерирована следующая ошибка компиляции

no match for 'operator=' ... note: no known conversion for argument 1 from 'A' to 'const B&'

Нет причин ожидать такой ошибки, потому что - это известное преобразование из *От 1008 * до const B&.B имеет конструктор преобразования для A:

B(const A& x)

Единственное соединение между ними состоит в том, что в BI используется оператор, который преобразует B в A

... и второе соединение между ними заключается в том, что в B вы используете конструктор, который преобразует A в B.

Правильно, что это конверсионный сундуктор.Но почему это вызывается, когда происходит присвоение bar = foo.

Поскольку данный операнд присваивания имеет тип A, а объявленный тип аргумента - const B&.

В тех случаях, когда тип аргумента не соответствует объявлению, компилятор проверяет, существует ли последовательность неявного преобразования , которую можно использовать для преобразования аргумента.Поскольку A неявно преобразуется в B, такая последовательность неявного преобразования существует и будет использоваться.

0 голосов
/ 24 января 2019

Это не конструктор копирования, который вы имеете, это конструктор преобразования. Вызывается конструктор преобразования, потому что вы можете превратить A в B.

Сделайте это:

explicit B(const A& x)

А теперь вы запретите неявное преобразование из A в B.

Когда вы делаете:

bar = foo;

Компилятор ищет разумные операции и допускает самое большее одно преобразование. Он может использовать присвоение копии из B и знает, что может создать B из A (потому что конструктор не явный), поэтому он делает это молча.

Как сказал @lubgr, у clang-tidy есть правило проверять их, и это часть основных рекомендаций C ++ .

...