Пространства имён
Варианты
Действия

определяемая пользователем функция преобразования

Материал из cppreference.com
< cpp‎ | language
 
 
Язык С++
Общие темы
Управление потоком
Операторы условного выполнения
if
Операторы итерации (циклы)
Операторы перехода
Функции
Объявление функции
Выражение лямбда-функции
Спецификатор inline
Спецификации динамических исключений (до C++17*)
Спецификатор noexcept (C++11)
Исключения
Пространства имён
Типы
Спецификаторы
decltype (C++11)
auto (C++11)
alignas (C++11)
Спецификаторы длительности хранения
Инициализация
Выражения
Альтернативные представления
Литералы
Логические - Целочисленные - С плавающей запятой
Символьные - Строковые - nullptr (C++11)
Определяемые пользователем (C++11)
Утилиты
Атрибуты (C++11)
Types
Объявление typedef
Объявление псевдонима типа (C++11)
Casts
Неявные преобразования - Явные преобразования
static_cast - dynamic_cast
const_cast - reinterpret_cast
Выделение памяти
Классы
Свойства функции класса
explicit (C++11)
static
Специальные функции-элементы
Шаблоны
Разное
 
Выражения
Общие
Категории значений (lvalue, rvalue, xvalue)
Порядок оценки (точки последовательности)
Константные выражения
Потенциально оцениваемые выражения
Первичные выражения
Лямбда-выражения(C++11)
Литералы
Целочисленные литералы
Литералы с плавающей запятой
Логические литералы
Символьные литералы, включая управляющие последовательности
Строковые литералы
Литерал нулевого указателя(C++11)
Пользовательский литерал(C++11)
Операторы
a=b, a+=b, a-=b, a*=b, a/=b, a%=b, a&=b, a|=b, a^=b, a<<=b, a>>=b
++a, --a, a++, a--
+a, -a, a+b, a-b, a*b, a/b, a%b, ~a, a&b, a|b, a^b, a<<b, a>>b
a||b, a&&b, !a
a==b, a!=b, a<b, a>b, a<=b, a>=b, a<=>b (начиная с C++20)
a[b], *a, &a, a->b, a.b, a->*b, a.*b
a(...), a,b, a?b:c
выражение new
выражение delete
выражение throw
alignof
sizeof
sizeof...(C++11)
typeid
noexcept(C++11)
Выражения свёртки(C++17)
Альтернативные представления операторов
Приоритет и ассоциативность
Перегрузка операторов
Сравнение по умолчанию(C++20)
Преобразования
Неявные преобразования
Обычные арифметические преобразования
const_cast
static_cast
reinterpret_cast
dynamic_cast
Явные преобразования: (T)a, T(a), auto(a), auto{a} (начиная с C++23)
Пользовательское преобразование
 

Делает возможным неявное преобразование или явное преобразование из классового типа в другой тип.

[править] Синтаксис

Функция преобразования объявляется как нестатическая функция-элемент или шаблон функции-элемента без параметров, без явного возвращаемого типа и с именем в форме:

operator идентификатор-преобразуемого-типа (1)
explicit operator идентификатор-преобразуемого-типа (2) (начиная с C++11)
explicit ( выражение ) operator идентификатор-преобразуемого-типа (3) (начиная с C++20)
1) Объявляет определяемую пользователем функцию преобразования, которая участвует во всех неявных и явных преобразованиях.
2) Объявляет пользовательскую функцию преобразования, которая участвует только в прямой инициализации и явных преобразованиях.
3) Объявляет пользовательскую функцию преобразования, которая является условно-явной.

идентификатор-преобразуемого-типа это идентификатор-типа, за исключением операторов функции и массива [] или (), которые не разрешены в его деклараторе (таким образом, преобразование в типы, такие как указатель на массив, требует псевдонима/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 (декларатора функции) или
как идентификатор-преобразуемого-типа
анализируется как часть
идентификатора-преобразуемого-типа