определяемая пользователем функция преобразования
Делает возможным неявное преобразование или явное преобразование из классового типа в другой тип.
Синтаксис
Функция преобразования объявляется как нестатическая функция-элемент или шаблон функции-элемента без параметров, без явного возвращаемого типа и с именем в форме:
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 { return 7; }
// явное преобразование
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 равным 7
int 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(const struct 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.operator int * 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 { return 10; }
// OK: выведенный тип возвращаемого значения
operator decltype(auto)() const { return 10l; }
};
Примечание: шаблон функции преобразования не может иметь выведенный тип возвращаемого значения. |
(начиная с 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 (декларатора функции) или как идентификатор-преобразуемого-типа |
анализируется как часть идентификатора-преобразуемого-типа |