15.9.2. Конструктор как конвертер

15.9.2. Конструктор как конвертер

Набор конструкторов класса, принимающих единственный параметр, например, SmallInt(int) класса SmallInt, определяет множество неявных преобразований в значения типа SmallInt. Так, конструктор SmallInt(int) преобразует значения типа int в значения типа SmallInt.

extern void calc( SmallInt );

int i;

// необходимо преобразовать i в значение типа SmallInt

// это достигается применением SmallInt(int)

calc( i );

При вызове calc(i) число i преобразуется в значение типа SmallInt с помощью конструктора SmallInt(int), вызванного компилятором для создания временного объекта нужного типа. Затем копия этого объекта передается в calc(), как если бы вызов функции был записан в форме:

// Псевдокод на C++

// создается временный объект типа SmallInt

{

SmallInt temp = SmallInt( i );

calc( temp );

}

Фигурные скобки в этом примере обозначают время жизни данного объекта: он уничтожается при выходе из функции.

Типом параметра конструктора может быть тип некоторого класса:

class Number {

public:

// создание значения типа Number из значения типа SmallInt

Number( const SmallInt & );

// ...

};

В таком случае значение типа SmallInt можно использовать всюду, где допустимо значение типа Number:

extern void func( Number );

SmallInt si(87);

int main()

{ // вызывается Number( const SmallInt & )

func( si );

// ...

}

Если конструктор используется для выполнения неявного преобразования, то должен ли тип его параметра точно соответствовать типу подлежащего преобразованию значения? Например, будет ли в следующем коде вызван SmallInt(int), определенный в классе SmallInt, для приведения dobj к типу SmallInt?

extern void calc( SmallInt );

double dobj;

// вызывается ли SmallInt(int)? Да

// dobj преобразуется приводится от double к int

// стандартным преобразованием

calc( dobj );

Если необходимо, к фактическому аргументу применяется последовательность стандартных преобразований до того, как вызвать конструктор, выполняющий определенное пользователем преобразование. При обращении к функции calc()употребляется стандартное преобразование dobj из типа double в тип int. Затем уже для приведения результата к типу SmallInt вызывается SmallInt(int).

Компилятор неявно использует конструктор с единственным параметром для преобразования его типа в тип класса, к которому принадлежит конструктор. Однако иногда удобнее, чтобы конструктор Number(const SmallInt&) можно было вызывать только для инициализации объекта типа Number значением типа SmallInt, но ни в коем случае не для выполнения неявных преобразований. Чтобы избежать такого употребления конструктора, объявим его явным (explicit):

class Number {

public:

// никогда не использовать для неявных преобразований

explicit Number( const SmallInt & );

// ...

};

Компилятор никогда не применяет явные конструкторы для выполнения неявных преобразований типов:

extern void func( Number );

SmallInt si(87);

int main()

{ // ошибка: не существует неявного преобразования из SmallInt в Number

func( si );

// ...

}

Однако такой конструктор все же можно использовать для преобразования типов, если оно запрошено явно в форме оператора приведения типа:

SmallInt si(87);

int main()

{ // ошибка: не существует неявного преобразования из SmallInt в Number

func( si );

func( Number( si ) ); // правильно: приведение типа

func( static_cast Number ( si ) ); // правильно: приведение типа

}