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

Явное преобразование типов

Материал из 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)
Пользовательское преобразование
 

Преобразует типы, используя комбинацию явных и неявных преобразований.

Содержание

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

( целевой-тип ) выражение (1)
целевой-тип ( список-выражений (необязательно) ) (2)
целевой-тип { список-выражений (необязательно) } (3) (начиная с C++11)
имя-шаблона ( список-выражений (необязательно) ) (4) (начиная с C++17)
имя-шаблона { список-выражений (необязательно) } (5) (начиная с C++17)
auto ( выражение ) (6) (начиная с C++23)
auto { выражение } (7) (начиная с C++23)

Возвращает значение типа целевой-тип.

[править] Объяснение

1) Когда встречается выражение приведения в стиле C компилятор пытается интерпретировать его как выражения приведения в следующем порядке:
a) const_cast<целевой-тип>(выражение);
b) static_cast<целевой-тип>(выражение), с расширениями: указатель или ссылка на производный класс дополнительно могут быть приведены к указателю или ссылке на однозначный базовый класс (и наоборот), даже если базовый класс недоступен (то есть это приведение игнорирует спецификатор частного наследования). То же самое относится к приведению указателя на элемент для указателя на элемент однозначного невиртуального базового класса;
c) static_cast (с расширениями), за которым следует const_cast;
d) reinterpret_cast<целевой-тип>(выражение);
e) reinterpret_cast за которым следует const_cast.
Выбирается первый вариант, удовлетворяющий требованиям соответствующего оператора приведения, даже если он не может быть скомпилирован (смотрите пример). Если приведение может быть интерпретировано более чем одним способом как static_cast, за которым следует const_cast, оно не может быть скомпилировано.
Кроме того, нотация приведения в стиле C позволяет выполнять приведение от, к и между указателями на неполный тип класса. Если и выражение и целевой-тип являются указателями на неполные типы классов, не указано, будет ли выбран static_cast или reinterpret_cast.
2) Функциональное выражение приведения состоит из спецификатора фундаментального типа или его спецификатора typedef (другими словами, имя типа из одного слова, то есть такие случаи, как unsigned int(выражение) и int*(выражение) недопустимы), за которым следует список выражений через запятую в круглых скобках.
  • Если в скобках указано ровно одно выражение, это выражение приведения точно эквивалентно соответствующему выражению приведения в стиле C.
  • Если в скобках указано более одного выражения или список инициализации в фигурных скобках (начиная с C++11) в скобках, целевой-тип должен быть классом с соответствующим образом объявленным конструктором. Это выражение является значением prvalue типа целевой-тип обозначающее временный объект (до C++17)чей объект результата (начиная с C++17) инициализируется напрямую списком-выражений.
  • Если в круглых скобках нет выражения: если целевой-тип именует полный тип объекта, не являющийся массивом, это выражение является значением prvalue типа целевой-тип, обозначающее временный объект (до C++17)чей объект результата (возможно, с добавленными cv-квалификаторами) (начиная с C++17) этого типа. Если целевой-тип является типом объекта, объект инициализированный значением. Если целевой-тип (возможно, cv-квалифицированный) void, выражение представляет собой void prvalue без объекта результата (начиная с C++17).
3) Имя типа, состоящее из одного слова, за которым следует список инициализации в фигурных скобках, является значением prvalue указанного типа, обозначающим временный объект (до C++17)объект результата (начиная с C++17), который является списком с прямой инициализацией указанным списком инициализации в фигурных скобках. Если целевой-тип (возможно, cv-квалифицированный) это void, выражение представляет собой void prvalue без объекта результата (начиная с C++17). Это единственное выражение приведения, которое может создать массив значений prvalue. (до C++20)
4,5) То же, что и (2,3), за исключением того, что сначала выполняется вывод аргумента шаблона класса.
6,7) Спецификатор auto заменяется выведенным типом придуманной переменной x, объявленной с помощью auto x(выражение); (которое никогда не интерпретируется как объявление функции) или auto x{выражение}; соответственно. Результатом всегда является prvalue объектного типа.

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

  • lvalue, если целевой-тип это ссылочный тип lvalue или ссылка rvalue на функциональный тип (начиная с C++11);
  • xvalue, если целевой-тип является ссылкой rvalue на тип объекта;
(начиная с C++11)
  • иначе prvalue.

[править] Разрешение неоднозначности

[править] Неоднозначное объявление оператора

В случае неоднозначности между оператором выражения с выражением приведения в стиле функции в качестве крайнего левого подвыражения и оператором объявления неоднозначность разрешается путём обработки его как объявления. Это устранение неоднозначности является чисто синтаксическим: оно не учитывает значение имён, встречающихся в операторе, кроме того, являются ли они именами типов:

struct M {};
struct L { L(M&); };
 
M n;
void f()
{
    M(m);    // объявление, эквивалентно M m;
    L(n);    // неправильно оформленное объявление, эквивалентно L n;
    L(l)(m); // все ещё объявление, эквивалентно L l((m));
}

Однако если самый внешний декларатор в неоднозначном операторе объявления имеет завершающий тип возвращаемого значения, этот оператор будет рассматриваться как оператор объявления только в том случае, если завершающий тип возвращаемого значения начинается с auto:

 
struct M;
 
struct S
{
    S* operator()();
    int N;
    int M;
 
    void mem(S s)
    {
        auto(s)()->M; // выражение (S::M скрывает ::M), неверное до C++23
    }
};
 
void f(S s)
{
    {
        auto(s)()->N; // выражение, неверное до C++23
        auto(s)()->M; // объявление функции, эквивалентное M s();
    }
    {
        S(s)()->N;    // выражение
        S(s)()->M;    // выражение
    }
}
(начиная с C++11)

[править] Неоднозначный параметр функции

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

struct S
{
    S(int);
};
 
void foo(double a)
{
    S w(int(a)); // объявление функции: имеет параметр `a` типа int
    S x(int());  // объявление функции: имеет безымянный параметр типа int(*)(),
                 // который настраивается из int()
 
    // Способы избежать двусмысленности:
    S y((int(a))); // объявление объекта: дополнительная пара скобок
    S y((int)a);   // объявление объекта: приведение в стиле C
    S z = int(a);  // объявление объекта: нет двусмысленности для этого синтаксиса
}

Однако если самый внешний декларатор в неоднозначном объявлении параметра имеет завершающий тип возвращаемого значения, неоднозначность будет разрешена только путём обработки его как объявления, если оно начинается с auto:

typedef struct BB { int C[2]; } *B, C;
 
void foo()
{
    S a(B()->C);    // объявление объекта: B()->C не может объявить параметр
    S b(auto()->C); // объявление функции: имеет безымянный параметр типа C(*)(),
                    // который мы скорректировали из C()
}
(начиная с C++11)

[править] Неоднозначный идентификатор типа

Неоднозначность может возникнуть из-за сходства между приведением в стиле функции и идентификатором типа. Решение состоит в том, что любая конструкция, которая может быть идентификатором типа в её синтаксическом контексте, должна считаться идентификатором типа:

// `int()` и `int(unsigned(a))` могут быть проанализированы как идентификаторы типа:
// `int()`            представляет функцию, возвращающую int
//                    и не принимающую аргументов
// `int(unsigned(a))` представляет функцию, возвращающую int
//                    и принимающую аргумент типа unsigned
void foo(signed char a)
{
    sizeof(int());            // идентификатор типа (неправильный формат)
    sizeof(int(a));           // выражение
    sizeof(int(unsigned(a))); // идентификатор типа (неправильный формат)
 
    (int()) + 1;            // идентификатор типа (неправильный формат)
    (int(a)) + 1;           // выражение
    (int(unsigned(a))) + 1; // идентификатор типа (неправильный формат)
}

Однако если самый внешний абстрактный декларатор в неоднозначном идентификаторе типа имеет завершающий возвращаемый тип, неоднозначность будет разрешена только путём обработки его как идентификатора типа, если он начинается с auto:

typedef struct BB { int C[2]; } *B, C;
 
void foo()
{
    sizeof(B()->C[1]);    // OK, sizeof(выражение)
    sizeof(auto()->C[1]); // ошибка: sizeof функции, возвращающей массив
}
(начиная с C++11)

[править] Примечание

Макрос Тестирования функциональности Значение Стандарт Функциональность
__cpp_auto_cast 202110L (C++23) auto(x) и auto{x}

[править] Пример

#include <cassert>
#include <iostream>
 
double f = 3.14;
unsigned int n1 = (unsigned int)f; // приведение в стиле C
unsigned int n2 = unsigned(f);     // приведение в функциональном стиле
 
class C1;
class C2;
C2* foo(C1* p)
{
    return (C2*)p; // приводит неполный тип к неполному типу
}
 
void cpp23_decay_copy_demo()
{
    auto inc_print = [](int& x, const int& y)
    {
        ++x;
        std::cout << "x:" << x << ", y:" << y << '\n';
    };
 
    int p{1};
    inc_print(p, p); // печатает x:2 y:2, потому что параметр y здесь является
                     // псевдонимом p
    int q{1};
    inc_print(q, auto{q}); // печатает x:2 y:1, auto{q} (C++23) приводит к prvalue,
                           // поэтому параметр y является копией q (а не псевдонимом q)
}
 
// В этом примере приведение в стиле C интерпретируется как
// static_cast, даже если оно будет работать как reinterpret_cast
struct A {};
struct I1 : A {};
struct I2 : A {};
struct D : I1, I2 {};
 
int main()
{
    D* d = nullptr;
//  A* a = (A*)d;                   // ошибка времени компиляции
    A* a = reinterpret_cast<A*>(d); // это компилируется
    assert(a == nullptr);
 
    cpp23_decay_copy_demo();
}

Вывод:

x:2 y:2
x:2 y:1

[править] Отчёты о дефектах

Следующие изменения поведения были применены с обратной силой к ранее опубликованным стандартам C++:

Номер Применён Поведение в стандарте Корректное поведение
CWG 1223 C++11 добавление завершающего типа возвращаемого значения
приводило к большей двусмысленности
это решено
CWG 2620 C++98 разрешение неоднозначных параметров функции могло
быть неправильно истолковано
улучшена формулировка

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

  • C++23 стандарт (ISO/IEC 14882:2023):
  • 7.6.1.4 Явное преобразование типов (функциональная нотация) [expr.type.conv]
  • 7.6.3 Явное преобразование типов (нотация приведения) [expr.cast]
  • C++20 стандарт (ISO/IEC 14882:2020):
  • 7.6.1.4 Явное преобразование типов (функциональная нотация) [expr.type.conv]
  • 7.6.3 Явное преобразование типов (нотация приведения) [expr.cast]
  • C++17 стандарт (ISO/IEC 14882:2017):
  • 8.2.3 Явное преобразование типов (функциональная нотация) [expr.type.conv]
  • 8.4 Явное преобразование типов (нотация приведения) [expr.cast]
  • C++14 стандарт (ISO/IEC 14882:2014):
  • 5.2.3 Явное преобразование типов (функциональная нотация) [expr.type.conv]
  • 5.4 Явное преобразование типов (нотация приведения) [expr.cast]
  • C++11 стандарт (ISO/IEC 14882:2011):
  • 5.2.3 Явное преобразование типов (функциональная нотация) [expr.type.conv]
  • 5.4 Явное преобразование типов (нотация приведения) [expr.cast]
  • C++03 стандарт (ISO/IEC 14882:2003):
  • 5.2.3 Явное преобразование типов (функциональная нотация) [expr.type.conv]
  • 5.4 Явное преобразование типов (нотация приведения) [expr.cast]
  • C++98 стандарт (ISO/IEC 14882:1998):
  • 5.2.3 Явное преобразование типов (функциональная нотация) [expr.type.conv]
  • 5.4 Явное преобразование типов (нотация приведения) [expr.cast]

[править] Смотрите также

преобразование const_cast добавляет или удаляет const[править]
преобразование static_cast выполняет основные преобразования[править]
приведение dynamic_cast выполняет полиморфные преобразования с контролируемым результатом[править]
преобразование reinterpret_cast выполняет общие низкоуровневые преобразования[править]
стандартные преобразования неявные преобразования из одного типа в другой[править]
Документация C по оператор приведения