Включен ли тип производного класса в тип базового класса в результате пользовательского преобразования? - PullRequest
1 голос
/ 07 апреля 2020

После прочтения некоторых цитат из стандарта cplusplus я запутался в преобразовании типа базового класса из производного типа класса. Относится ли это преобразование к определенному пользователем преобразованию?

Приведите цитату, которая меня смущает:

[class.conv] / 1 Можно указывать преобразования типов объектов класса. конструкторами и функциями преобразования. Эти преобразования называются пользовательскими преобразованиями и используются для неявных преобразований типов ...

[class.conv.ctor] / 3
Неявный конструктор копирования / перемещения ([class.copy] ) является конвертирующим конструктором.

#include <iostream>
struct Base{
   Base() = default;
   Base(Base const&){}
};
struct Derived:Base{
};
int main(){
   Derived d;
   Base b = d; //from d to b, is this a user-defined conversion? Before reading the standard, I think it's not, but now I'm confused about this.
}

Таким образом, согласно этим кавычкам, объект производного типа класса в тип базового класса принадлежит пользовательскому преобразованию. Если я пропущу что-то написанное в стандарте, в котором говорится, что тип производного класса к базовому типу класса не будет принадлежать пользовательскому преобразованию, исправьте меня.

Ответы [ 2 ]

1 голос
/ 07 апреля 2020

ОК, давайте разберем то, что говорит стандарт:

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

Теперь давайте притворяться, что мы ничего не знаем о значении этих слов. Это предложение говорит о концепции, называемой «преобразованием типов», но оно конкретно говорит о «преобразованиях типов объектов классов». Таким образом, мы говорим не о всех преобразованиях типов, а только о их подмножестве.

Затем говорится «может быть указано» и перечисляются несколько способов их указания. Следующее предложение:

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

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

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

Итак, что мы можем сказать из этого является то, что объекты класса могут иметь преобразования типов. Эти преобразования могут быть определены определенными вещами в классе. И этот конкретный тип преобразований типов называется «определяемыми пользователем преобразованиями».

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

Далее мы переходим к [class.conv.ctor] / 1 :

Конструктор объявленный без спецификатора функции явным образом указывает преобразование типов его параметров (если они есть) в тип его класса. Такой конструктор называется конвертирующим конструктором.

ОК, теперь у нас есть определение «конвертирующего конструктора». Действительно, учитывая это определение, параграф 3 (объявляющий, что не explicit конструкторы копирования / перемещения являются конвертирующими конструкторами) является избыточным; Приведенное выше определение проясняет, что они есть.

Быть «конвертирующим конструктором» - это свойство конструктора. Процесс пользовательского преобразования прописан, и он, безусловно, может вызвать «конструктор преобразования». Но ни в коем случае не утверждается и не подразумевается, что это процесс only , с помощью которого можно вызвать «конструктор преобразования».

Следовательно, тот факт, что конструктор копирования является «преобразованием» «Конструктор» не следует истолковывать как означающее, что все, что приводит к вызову конструктора копирования, само является преобразованием, определяемым пользователем. Определяемые пользователем преобразования происходят, когда стандарт сообщает , что они происходят.

В описываемом вами примере происходящее определено в [dcl.init] /17.6.2:

В противном случае, если инициализация является прямой инициализацией, или если это инициализация копирования, где версия cv-без квалификации исходного типа того же класса, или производный класс, класс места назначения, конструкторы рассматриваются. Применимые конструкторы перечисляются ([over.match.ctor]), и лучший выбирается через разрешение перегрузки. Выбранный таким образом конструктор вызывается для инициализации объекта, с выражением инициализатора или списком выражений в качестве аргументов. Если конструктор не применяется, или разрешение перегрузки неоднозначно, инициализация неверна.

Нигде в этом правиле Само не указано, что преобразование любого вида напрямую выполнила. Происходит разрешение перегрузки конструкторов с одним аргументом типа назначения. Правила разрешения перегрузки могут учитывать несколько преобразований, поскольку он пытается подогнать данный аргумент к различным возможностям параметров в наборе перегрузки. Но это общие c, связанные с любым разрешением перегрузки любого вызова функции.

То есть тот факт, что выбранная именно так функция считается «конструктором преобразования», не означает, что определяемое пользователем преобразование вызвало ее вызов.

0 голосов
/ 07 апреля 2020

Определяемые пользователем преобразования здесь не рассматриваются. Стандарт перечисляет два случая инициализации копирования (из которых синтаксис Base b = d; является одной формой). Это

[dcl.init]/17.16.2

В противном случае, если инициализация является прямой инициализацией, или если это инициализация с копированием, когда cv-неквалифицированная версия исходный тип - это тот же класс, или производный класс класса назначения, учитываются конструкторы. ...

и

[dcl.init]/17.16.3

В противном случае (т. Е. Для остальных случаев инициализации копирования), определяемые пользователем преобразования, которые могут преобразовывать из исходного типа в целевой тип ... [используются].

В этом случае, поскольку Derived является производным от Base, используется предыдущее предложение и не последний. Таким образом, используются только конструкторы Base, а не, например, любые operator Base(), которые могут быть определены в Derived.

...