Подтвердить что ты не робот

Неявное VS-явное преобразование

Стандартная библиотека С++ Николая М. Йосуттиса гласит:

Существует незначительная разница между

X x;
Y y(x) //explicit conversion

и

X x;
Y y = x; //implicit conversion

Далее, чтобы сказать: "Первый создает новый объект типа Y, используя явное преобразование из типа X, тогда как последнее создает новый объект типа Y, используя неявное преобразование".

Я немного смущен концепциями явного vs неявного преобразования, я думаю. В обоих случаях вы принимаете X и нажимаете его на Y как таковой - один использует конструктор Y, а один использует оператор присваивания.

Какая разница в том, как обращение обрабатывается в этих двух случаях, что делает его явным/неявным, и как это связано с созданием конструктора классов, определенного с помощью "явного" ключевого слова, если вообще?

4b9b3361

Ответ 1

используется Y-конструктор, и один использует оператор присваивания.

Неа. Во втором случае это не назначение, это инициализация, оператор присваивания (operator=) никогда не вызывается; вместо этого вызывается однопараметрический конструктор не explicit (который принимает тип X в качестве параметра).

Разница между инициализацией и присваиванием важна: в первом случае создается новый объект, и он начинает свою жизнь со значения, которое он инициализируется (отсюда и почему вызывается конструктор), в то время как назначение происходит когда объект присваивается (копируется) объекту, который уже существует и уже находится в определенном состоянии.

В любом случае две формы инициализации, которые вы написали, отличаются тем, что в первом случае вы явно вызываете конструктор, и, следовательно, любой конструктор является приемлемым; во втором случае вы вызываете конструктор неявно, так как вы не используете синтаксис конструктора "классический", а синтаксис инициализации.

В этом случае допустимы только однопараметрические конструкторы, не отмеченные знаком explicit. Такие конструкторы вызываются некоторыми "конструкторами преобразования", потому что они участвуют в неявных преобразованиях.

Как указано в этом другом ответе, любой конструктор, не помеченный как explicit, может принимать участие в неявном преобразовании, например. преобразование объекта, переданного функции в тип, ожидаемый такой функцией. На самом деле вы можете сказать, что это происходит во втором примере: вы хотите инициализировать (= создать со значением, скопированным из другого места) y с X, но X сначала нужно преобразовать в тип y, который выполняется с помощью неявного конструктора.

Этот тип неявного преобразования часто желателен: например, предположите, что для строкового класса с конструктором преобразования (т.е. не explicit) из const char *: любая функция, которая принимает параметр string, также может быть вызванный с "нормальной" C-строкой: из-за конструктора преобразования вызывающий использует C-строки, вызываемый получит свой объект string.

Тем не менее, в некоторых случаях конструкторы с одним параметром могут не подходить для преобразования: обычно это происходит, когда их единственный параметр не концептуально "преобразован" в тип создаваемого объекта, но это просто параметр для построения; например, об объекте потока файлов: возможно, он будет иметь конструктор, который принимает имя файла для открытия, но нет смысла говорить, что такая строка "преобразуется" в поток, который работает в этом файле.

Вы также можете найти более сложные сценарии, в которых эти неявные преобразования могут полностью испортить поведение, которое программист ожидает от разрешения перегрузки; примеры этого можно найти в ответах ниже того, которое я связал выше.

Более просто, также может случиться так, что некоторые конструкторы могут быть очень тяжелыми, поэтому разработчик классов может захотеть убедиться, что они явно вызваны. В этих случаях конструктор помечен как explicit, поэтому его можно использовать только при вызове "явно как конструктор" и не участвует в неявных преобразованиях.

Ответ 2

Первая форма - это прямая инициализация. Вторая - инициализация копирования.

Инициализация копирования неявно вызывает конструктор преобразования или оператор преобразования, а затем явно вызывает конструктор копирования (вызов конструктора копирования может быть отменен, но проверка доступности еще должна выполняться).

Рассмотрим третью возможность, которая является копией-инициализацией, но преобразование является явным:

Y y = Y(x);

или

Y y = (Y)x;

Ответ 3

используется оператор присваивания, хотя

Нет, это не так. Он вызывает конструктор напрямую.

Причина, по которой одна является явной, а одна - неявной, заключается в том, что неявные преобразования могут произойти, когда вы этого не хотите. Явные не могут. Самый простой пример: bool.

Скажем, вы изобретаете какой-то тип, который может быть либо истинным, либо ложным, как указатель. Затем давайте далее скажем, что вы решили, что для того, чтобы сделать жизнь проще для ваших пользователей, вы позволяете ей конвертировать в bool неявно. Это здорово - вплоть до момента, когда один из ваших пользователей делает что-то немое.

int i = 0;
i = i >> MyUDT();

Ой, подождите, почему это даже скомпилируется? Вы не можете перенести MyUDT вообще! Он компилируется, потому что bool является интегральным типом. Компилятор неявно преобразовал его в bool, а затем в нечто, что можно сдвинуть. Вышеприведенный код вопиющий тупой - мы хотим, чтобы люди могли конвертировать в bool, а не в bool и что-нибудь еще, что может хотеть сделать bool.

Вот почему явные операторы преобразования были добавлены в С++ 0x.

Ответ 4

Неявное литье не требует никакого оператора литья. Это литье обычно используется при преобразовании данных из более мелких интегральных типов в более крупные или производные типы в базовый тип.

int iVal = 100;   double dVal = iVal;

Конструктор преобразования явный предпочтительнее оператора неявного преобразования, потому что в последнем случае есть дополнительный вызов конструктору копирования. Неявные и явные конверсии