Явное преобразование типов
Преобразует значения между типами с помощью сочетания явных и неявных преобразований.
Синтаксис
( 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) |
Явно преобразует любое количество значений в значение целевого типа.
| type-id | - | идентификатор типа |
| unary-expression | - | унарное выражение (оператор верхнего уровня которого не имеет приоритета выше, чем у приведения в стиле C) |
| simple-type-specifier | - | спецификатор простого типа |
| expression-list | - | список выражений, разделённых запятыми (за исключением выражений с оператором-запятой без скобок) |
| initializer-list | - | список инициализирующих выражений, разделённых запятыми |
| designated-initializer-list | - | список именованных инициализирующих выражений, разделённых запятыми |
| identifier | - | идентификатор (возможно, квалифицированный), включая идентификаторы шаблонов |
Пояснение
const_cast<type-id >(unary-expression );static_cast<type-id >(unary-expression ), с расширениями: указатель или ссылка на производный класс дополнительно может быть приведён к указателю или ссылке на однозначный базовый класс (и наоборот), даже если базовый класс недоступен (то есть это приведение игнорирует спецификатор закрытого наследования). То же относится к приведению указателя на член к указателю на член однозначного невиртуального базового класса;reinterpret_cast<type-id >(unary-expression );T, определяемого заданным типом и инициализатором(начиная с C++17): |
| (до C++17) | ||
|
| (начиная с C++17) |
- Если приведение в функциональном стиле имеет синтаксис (2) и в скобках находится ровно одно выражение, это приведение эквивалентно соответствующему приведению в стиле C.
- В противном случае, если
Tявляется (возможно, cv-квалифицированным) типом void, результатом является rvalue(до C++11)prvalue(начиная с C++11) типа void, при этом инициализация не выполняется.
| (до C++11) |
| (начиная с C++11) |
- В противном случае, если
Tявляется ссылочным типом, приведение в функциональном стиле имеет тот же эффект, что и прямая инициализация вымышленной переменнойtтипаTзаданным инициализатором; результатом является инициализированнаяt.
| (до C++11) |
| (начиная с 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); // declaration, equivalent to M m;
L(n); // ill-formed declaration, equivalent to L n;
L(l)(m); // still a declaration, equivalent to L l((m));
}
| Однако, если внешний декларатор в неоднозначной инструкции-объявлении содержит возвращаемый тип в конце, инструкция считается инструкцией-объявлением только в том случае, если возвращаемый тип в конце начинается с 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)); // function declaration: has a parameter `a` of type int
S x(int()); // function declaration: has an unnamed parameter of type int(*)()
// that is adjusted from int()
// Ways to avoid ambiguity:
S y((int(a))); // object declaration: extra pair of parentheses
S y((int)a); // object declaration: C-style cast
S z = int(a); // object declaration: no ambiguity for this syntax
}
| Однако, если внешний декларатор в неоднозначном объявлении параметра содержит возвращаемый тип в конце, неоднозначность разрешается в пользу объявления только в том случае, если он начинается с auto: typedef struct BB { int C[2]; } *B, C;
void foo()
{
S a(B()->C); // object declaration: B()->C cannot declare a parameter
S b(auto()->C); // function declaration: has an unnamed parameter of type C(*)()
// that is adjusted from C()
}
| (начиная с C++11) |
Неоднозначный идентификатор типа
Неоднозначность может возникнуть из-за сходства приведения в функциональном стиле с идентификатором типа. Она разрешается так: любую конструкцию, которая в своём синтаксическом контексте может быть идентификатором типа, следует считать идентификатором типа:
// `int()` and `int(unsigned(a))` can both be parsed as type-id:
// `int()` represents a function returning int
// and taking no argument
// `int(unsigned(a))` represents a function returning int
// and taking an argument of type unsigned
void foo(signed char a)
{
sizeof(int()); // type-id (ill-formed)
sizeof(int(a)); // expression
sizeof(int(unsigned(a))); // type-id (ill-formed)
(int()) + 1; // type-id (ill-formed)
(int(a)) + 1; // expression
(int(unsigned(a))) + 1; // type-id (ill-formed)
}
| Однако, если внешний abstract-declarator в неоднозначном идентификаторе типа содержит возвращаемый тип в конце, неоднозначность разрешается в пользу идентификатора типа только в том случае, если он начинается с 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 преобразование | добавляет или удаляет const |
static_cast преобразование | выполняет базовые преобразования |
dynamic_cast преобразование | выполняет проверяемые полиморфные преобразования |
reinterpret_cast преобразование | выполняет общие низкоуровневые преобразования |
| стандартные преобразования | неявные преобразования из одного типа в другой |
| Документация по C для оператора приведения | |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/language/explicit_cast