определяемая пользователем функция преобразования
Делает возможным неявное преобразование или явное преобразование из классового типа в другой тип.
[править]Синтаксис
Функция преобразования объявляется как нестатическая функция-элемент или шаблон функции-элемента без параметров, без явного возвращаемого типа и с именем в форме:
operator идентификатор-преобразуемого-типа | (1) | ||||||||
explicit operator идентификатор-преобразуемого-типа | (2) | (начиная с C++11) | |||||||
explicit ( выражение) operator идентификатор-преобразуемого-типа | (3) | (начиная с C++20) | |||||||
идентификатор-преобразуемого-типа это идентификатор-типа, за исключением операторов функции и массива []
или ()
, которые не разрешены в его деклараторе (таким образом, преобразование в типы, такие как указатель на массив, требует псевдонима/typedef типа или шаблон идентичности: смотрите ниже). Независимо от typedef, идентификатор-преобразуемого-типа не может представлять тип массива или функции.
Хотя возвращаемый тип не разрешён в объявлении определяемой пользователем функции преобразования, последовательность-спецификаторов-объявления или грамматика объявления может присутствовать и может включать любой спецификатор, отличный от спецификатора-типа или ключевого слова static
. В частности, помимо explicit
, спецификаторы inline
, virtual
, constexpr
(начиная с C++11), consteval
(начиная с C++20), и friend
также разрешены (обратите внимание, что для friend
требуется полное имя: friend A::operator B();).
Когда такая функция-элемент объявлена в классе X, она выполняет преобразование из X в идентификатор-преобразуемого-типа:
struct X {// неявное преобразование operator int()const{return7;} // явное преобразованиеexplicit operator int*()const{return nullptr;} // Ошибка: оператор массива не разрешён в идентификаторе-преобразуемого-типа// operator int(*)[3]() const { return nullptr; } using arr_t =int[3]; operator arr_t*()const{return nullptr;}// ОК, если это делается через typedef// operator arr_t () const; // Ошибка: преобразование в массив запрещено во всех случаях}; int main(){ X x; int n =static_cast<int>(x);// OK: устанавливает n равным 7int m = x;// OK: устанавливает n равным 7 int* p =static_cast<int*>(x);// OK: устанавливает p равным null// int* q = x; // Ошибка: нет неявного преобразования int(*pa)[3]= x;// OK}
[править]Объяснение
Определяемая пользователем функция преобразования вызывается на втором этапе неявного преобразования, состоящего из нуля или одного конструктора преобразования или нуля или одной определяемой пользователем функции преобразования.
Если и функции преобразования, и конструкторы преобразования могут использоваться для выполнения определённого пользователем преобразования, функции преобразования и конструкторы учитываются разрешением перегрузки в контекстах инициализации копированием и инициализации ссылки, но в контекстах прямой-инициализации учитываются только конструкторы.
struct To { To()=default; To(conststruct From&){}// конструктор преобразования}; struct From { operator To()const{return To();}// функция преобразования}; int main(){ From f; To t1(f);// прямая инициализация: вызывается конструктор// Примечание: если конструктор преобразования недоступен, будет выбран конструктор// неявного копирования, и будет вызвана функция преобразования для подготовки его// аргумента. // To t2 = f; // инициализация копированием: неоднозначно// Примечание: если функция преобразования имеет неконстантный тип, т.е.// From::operator To();, в данном случае будет выбрана она вместо конструктора To t3 =static_cast<To>(f);// прямая инициализация: вызывает конструкторconst To& r = f;// инициализация ссылки: неоднозначно}
Функция преобразования в собственный (возможно, cv-квалифицированный) класс (или в ссылку на него), в базовый своего класса (или в ссылку на него) и в тип void может быть определена, но не может быть выполнена как часть последовательности преобразования, за исключением некоторых случаев, через виртуальную диспетчеризацию:
struct D; struct B {virtual operator D()=0;}; struct D : B { operator D() override {return D();}}; int main(){ D obj; D obj2 = obj;// не вызывает D::operator D() B& br = obj; D obj3 = br;// вызывает D::operator D() через виртуальную диспетчеризацию}
Его также можно вызвать, используя синтаксис вызова функции-элемента:
struct B {}; struct X : B { operator B&(){return*this;};}; int main(){ X x; B& b1 = x;// не вызывает X::operatorB&() B& b2 =static_cast<B&>(x);// не вызывает X::operatorB& B& b3 = x.operator B&();// вызывает X::operatorB&}
При явном вызове функции преобразования идентификатор типа является жадным: это самая длинная возможная последовательность токенов, которая является допустимым идентификатором типа (включая атрибуты, если они есть):
& x.operatorint* a;// анализируется как & (x.operator int*) a// не как & (x.operator int) * a operator int[[noreturn]]();// ошибка: атрибут noreturn применён к типу
Заполнитель auto можно использовать в идентификаторе-преобразуемого-типа, указывая на выведенный тип возвращаемого значения: struct X { operator int();// OK operator auto()->short;// ошибка: завершающий возвращаемый тип не является// частью синтаксиса// OK: выведенный тип возвращаемого значения operator auto()const{return10;}// OK: выведенный тип возвращаемого значения operator decltype(auto)()const{return10l;}}; Примечание: шаблон функции преобразования не может иметь выведенный тип возвращаемого значения. | (начиная с C++14) |
Функции преобразования могут наследоваться и могут быть virtual, но не могут быть static. Функция преобразования в производном классе не скрывает функцию преобразования в базовом классе, если только они не преобразуют в один и тот же тип.
Функция преобразования может быть функцией-элементом шаблона, например, std::auto_ptr<T>::operator auto_ptr<Y>
. Смотрите элемент шаблона и вывод аргумента шаблона о применимых специальных правилах.
[править]Отчёты о дефектах
Следующие изменения поведения были применены с обратной силой к ранее опубликованным стандартам C++:
Номер | Применён | Поведение в стандарте | Корректное поведение |
---|---|---|---|
CWG 296 | C++98 | функции преобразования могут быть статическими | они не могут быть объявлены статическими |
CWG 2016 | C++98 | функции преобразования не могут указывать возвращаемые типы, но типы присутствуют в идентификаторе-преобразуемого-типа | возвращаемые типы не могут быть указаны в спецификаторах объявлений функций преобразования |
CWG 2175 | C++11 | было неясно, анализируется ли [[noreturn]] в operator int[[noreturn]](); как часть декларатора-noptr (декларатора функции) или как идентификатор-преобразуемого-типа | анализируется как часть идентификатора-преобразуемого-типа |