Пользовательская последовательность конверсий - PullRequest
0 голосов
/ 14 ноября 2018

До того, как я изучил ключевое слово explicit, мой учитель сказал: "Компилятор не выполняет последовательное преобразование, определенное пользователем".Если это правда, есть ли ошибки в моем коде?Или я неправильно понял своего учителя?Я работаю в VS2017.

#include<iostream>
#include <string>

class Myclass {
public:
    Myclass() {
        std::cout << "Myclass" << std::endl;
    }
};

class Myclass1 {
public:
    Myclass1(Myclass m) {
        std::cout << "Myclass1" << std::endl;
    }
};
class Myclass2{
public:
    Myclass2(Myclass1 m) {
        std::cout << "Myclass2" << std::endl;
    }
};

int main() {
    Myclass2 m2 = Myclass{};
} 

Ответы [ 3 ]

0 голосов
/ 14 ноября 2018

Строка

 Myclass2 m2 = Myclass{};

означает копия-инициализация .Цитирование cppreference.com :

Если T является типом класса, а версия cv-unqualified типа other не является T или получена изT [...], определяемые пользователем последовательности преобразования, которые могут преобразовывать из типа other в T [...], проверяются, и лучшая из них выбирается с помощью разрешения перегрузки.

Цитирование далее :

Пользовательское преобразование состоит из нуля или одного неявного конструктора с одним аргументом или неявной функции преобразованияcall.

Итак, Myclass2 m2 = Myclass{}; неприемлемо, потому что это потребует двух пользовательских преобразований.


Теперь давайте взглянем на

Myclass2 m2 {Myclass{}};

предложено в ответе Afshin .Это прямая инициализация .Правила: разные :

Исследуются конструкторы T, и наилучшее совпадение выбирается разрешением перегрузки.Затем вызывается конструктор для инициализации объекта.

Конструктор Myclass2 принимает Myclass1, и вам нужно одно пользовательское преобразование, чтобы получить Myclass1 из Myclass.Следовательно, он компилируется.


Обратите внимание, что в VS копирование-инициализация обрабатывается как прямая инициализация , если режим соответствия (/premissive-) не активирован(по умолчанию).Итак, VS принимает Myclass2 m2 = Myclass{};, считая его прямой инициализацией .См. этот документ для примеров.

0 голосов
/ 14 ноября 2018

Другие ответы хоронят лед: код, который вы написали, действительно недействителен . MSVC принимает его по умолчанию, но MSVC делает это неправильно. Вы можете заставить MSVC быть более строгим, используя переключатель командной строки /permissive-. ( Вы должны использовать этот переключатель .)

Другие компиляторы (GCC, clang), отклонить его.

Все компиляторы принимают код после того, как вы измените инициализацию копирования на прямую инициализацию, как показано в других ответах.

0 голосов
/ 14 ноября 2018

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

Ваш учитель прав. В вашем примере кода это означает, что Myclass не может быть преобразовано в Myclass1 при назначении в:

Myclass2 m2 = Myclass{};

Поскольку конструктор ожидает Myclass1 при создании Myclass2, а компилятор не может последовательно преобразовать Myclass в Myclass1 и затем использовать его для создания Myclass2. Но если у вас есть следующая строка:

Myclass1 m2 = Myclass{};

Это будет работать, потому что конструктор Myclass1 принимает Myclass в качестве аргумента.

Обновление:

Вы можете спросить, почему это работает:

Myclass2 m2 {Myclass{}};

Поскольку в этом случае вызывается конструктор, и преобразование может выполняться неявным образом, если вы не объявите Myclass1 как explicit, что приведет к ошибке компиляции кода (спасибо Fureeish за напоминание), но в:

Myclass2 m2 = Myclass{};

похоже на вызов конструктора копирования, который нуждается в ссылке. так что если вы напишите так, это сработает:

Myclass2 m2 = Myclass1(Myclass{});

Как упоминалось EVG , VS 10 принимается Myclass2 m2 = Myclass{};, если режим соответствия (/ permissive-) не активирован.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...