Namespaces
Variants

Explicit type conversion

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

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

Содержание

Синтаксис

( type-id ) unary-expression (1)
simple-type-specifier ( expression-list  (необязательно) )
simple-type-specifier ( initializer-list  (необязательно) )
(2) (до C++11)
(начиная с C++11)
simple-type-specifier { initializer-list  (необязательно) } (3) (начиная с C++11)
simple-type-specifier { designated-initializer-list } (4) (начиная с C++20)
typename identifier ( initializer-list  (необязательно) ) (5) (начиная с C++11)
typename identifier { initializer-list  (необязательно) } (6) (начиная с C++11)
typename identifier { designated-initializer-list } (7) (начиная с C++20)

Явно преобразует любое количество значений в значение целевого типа.

1) Явное преобразование типов (нотация приведения), также называемое C-style cast .
2-7) Явное преобразование типов (функциональная нотация), также называемое function-style cast .
type-id - a type-id
unary-expression - унарное выражение (чей оператор верхнего уровня не имеет приоритета выше, чем у C-style приведения)
simple-type-specifier - a simple type specifier
expression-list - список выражений, разделённых запятыми (за исключением не заключённых в скобки comma expressions )
initializer-list - список, разделённый запятыми, initializer clauses
designated-initializer-list - список, разделённый запятыми, designated initializer clauses
identifier - (возможно, квалифицированный) идентификатор (включая template identifiers )

Объяснение

1) Когда встречается приведение в стиле C, компилятор пытается интерпретировать его как следующие выражения приведения, в указанном порядке:
а) const_cast < type-id  > ( unary-expression  ) ;
б) static_cast < type-id  > ( unary-expression  ) , с расширениями: указатель или ссылка на производный класс дополнительно может быть приведён к указателю или ссылке на однозначный базовый класс (и наоборот), даже если базовый класс недоступен (то есть это приведение игнорирует спецификатор закрытого наследования). То же самое применяется к приведению указателя на член к указателю на член однозначного невиртуального базового класса;
c) a static_cast (с расширениями) за которым следует const_cast ;
d) reinterpret_cast < type-id  > ( unary-expression  ) ;
e) a reinterpret_cast с последующим const_cast .
Выбирается первый вариант, удовлетворяющий требованиям соответствующего оператора приведения, даже если он некорректен (см. пример). Если используется static_cast с последующим const_cast и преобразование может быть интерпретировано более чем одним способом, такое преобразование считается некорректным.
Кроме того, C-стиль приведения может преобразовывать указатели на неполный тип класса в, из и между собой. Если и type-id , и тип unary-expression являются указателями на неполные типы классов, не определено, будет выбрано static_cast или reinterpret_cast .
2-7) Функциональное приведение типа задаёт тип ( simple-type-specifier  или identifier  (since C++11) ) и инициализатор (остальные части), оно конструирует значение целевого типа T , который определяется из указанного типа и инициализатора (since C++17) :

T — это указанный тип.

(until C++17)

T определяется следующим образом:

  • Если указанный тип является placeholder для выводимого типа класса, T — это возвращаемый тип функции, выбранной в результате разрешения перегрузки для class template deduction .
  • В противном случае, если указанный тип содержит placeholder type , T — это выведенный тип.
(since C++23)
  • В противном случае, T — это указанный тип.
(since C++17)
Результат преобразования определяется следующим образом:
  • Если функциональное приведение имеет синтаксис (2) и в скобках находится ровно одно выражение, это приведение эквивалентно соответствующему C-стилю приведения.
  • В противном случае, если T является (возможно, cv-квалифицированным) void , результат представляет собой rvalue (до C++11) prvalue (начиная с C++11) типа void , который не выполняет инициализацию.
  • Если инициализатор не является ( ) , программа является некорректной.
(до C++11)
  • Если инициализатор не является ( ) или { } после раскрытия пакета (если применимо), программа является некорректной.
(начиная с C++11)
  • В противном случае, если T является ссылочным типом, функциональное приведение имеет тот же эффект, что и прямая инициализация созданной переменной t типа T из указанного инициализатора, и результатом является инициализированная t .
  • Результат является lvalue.
(до C++11)
  • Если T является типом lvalue-ссылки или rvalue-ссылкой на тип функции, результат является lvalue.
  • В противном случае результат является xvalue.
(начиная с C++11)
  • В противном случае результат представляет собой rvalue (до C++11) prvalue (начиная с C++11) типа T обозначающий временный объект (до C++17) чей результирующий объект (начиная с C++17) прямо инициализируется указанным инициализатором.

Разрешение неоднозначностей

Неоднозначное объявление

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

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

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

struct M;
struct S
{
    S* operator()();
    int N;
    int M;
    void mem(S s)
    {
        auto(s)()->M; // expression (S::M hides ::M), invalid before C++23
    }
};
void f(S s)
{
    {
        auto(s)()->N; // expression, invalid before C++23
        auto(s)()->M; // function declaration, equivalent to M s();
    }
    {
        S(s)()->N;    // expression
        S(s)()->M;    // expression
    }
}
(начиная с 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-style приведение типа
    S z = int(a);  // объявление объекта: нет неоднозначности для этого синтаксиса
}

Однако, если самый внешний декларатор в неоднозначном объявлении параметра имеет trailing return type , неоднозначность будет разрешена только путём трактовки его как объявления, если оно начинается с 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)

Неоднозначный type-id

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

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

Однако, если самый внешний abstract-declarator в неоднозначном type-id имеет trailing return type , неоднозначность будет разрешена только путем трактовки его как type-id, если он начинается с auto :

typedef struct BB { int C[2]; } *B, C;
void foo()
{
    sizeof(B()->C[1]);    // OK, sizeof(expression)
    sizeof(auto()->C[1]); // error: sizeof of a function returning an array
}
(начиная с 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-style cast
unsigned int n2 = unsigned(f);     // function-style cast
class C1;
class C2;
C2* foo(C1* p)
{
    return (C2*)p; // casts incomplete type to incomplete type
}
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); // prints x:2 y:2, because param y here is an alias of p
    int q{1};
    inc_print(q, auto{q}); // prints x:2 y:1, auto{q} (C++23) casts to prvalue,
                           // so the param y is a copy of q (not an alias of q)
}
// In this example, C-style cast is interpreted as static_cast
// even though it would work as reinterpret_cast
struct A {};
struct I1 : A {};
struct I2 : A {};
struct D : I1, I2 {};
int main()
{
    D* d = nullptr;
//  A* a = (A*)d;                   // compile-time error
    A* a = reinterpret_cast<A*>(d); // this compiles
    assert(a == nullptr);
    cpp23_decay_copy_demo();
}

Вывод:

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

Отчёты о дефектах

Следующие отчеты об изменениях поведения, влияющие на дефекты, были применены задним числом к ранее опубликованным стандартам C++.

DR Применяется к Поведение в опубликованной версии Корректное поведение
CWG 1223
( P2915R0 )
C++11 добавление завершающего возвращаемого типа ввело больше неоднозначностей разрешает их
CWG 1893 C++11 функциональное приведение не учитывало развертывания пакетов учитывает их
CWG 2351 C++11 void { } было некорректным сделано корректным
CWG 2620 C++98 разрешение неоднозначных параметров функции
могло быть неверно истолковано
улучшена формулировка
CWG 2828 C++98 C-стиль приведения был некорректным, если существуют множественные интерпретации
static_cast с последующим const_cast ,
независимо от того, используются ли эти преобразования фактически
учитывает только
преобразования,
которые могут быть использованы
CWG 2894 C++98 функциональные приведения могли создавать rvalue-ссылки могут создавать только lvalue-ссылки

Ссылки

  • Стандарт C++23 (ISO/IEC 14882:2024):
  • 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 conversion добавляет или удаляет const
static_cast conversion выполняет базовые преобразования
dynamic_cast conversion выполняет проверенные полиморфные преобразования
reinterpret_cast conversion выполняет общие низкоуровневые преобразования
стандартные преобразования неявные преобразования из одного типа в другой
документация по C для оператора приведения