Явное преобразование типов
Преобразует типы, используя комбинацию явных и неявных преобразований.
Синтаксис
( целевой-тип ) выражение
|
(1) | ||||||||
целевой-тип ( список-выражений (необязательно) )
|
(2) | ||||||||
целевой-тип { список-выражений (необязательно) }
|
(3) | (начиная с C++11) | |||||||
имя-шаблона ( список-выражений (необязательно) )
|
(4) | (начиная с C++17) | |||||||
имя-шаблона { список-выражений (необязательно) }
|
(5) | (начиная с C++17) | |||||||
auto ( выражение )
|
(6) | (начиная с C++23) | |||||||
auto { выражение }
|
(7) | (начиная с C++23) | |||||||
Возвращает значение типа целевой-тип.
Объяснение
const_cast<целевой-тип>(выражение);static_cast<целевой-тип>(выражение), с расширениями: указатель или ссылка на производный класс дополнительно могут быть приведены к указателю или ссылке на однозначный базовый класс (и наоборот), даже если базовый класс недоступен (то есть это приведение игнорирует спецификатор частного наследования). То же самое относится к приведению указателя на элемент для указателя на элемент однозначного невиртуального базового класса;static_cast (с расширениями), за которым следует const_cast;reinterpret_cast<целевой-тип>(выражение);reinterpret_cast за которым следует const_cast.static_cast, за которым следует const_cast, оно не может быть скомпилировано.static_cast или reinterpret_cast.unsigned int(выражение) и int*(выражение) недопустимы), за которым следует список выражений через запятую в круглых скобках.
- Если в скобках указано ровно одно выражение, это выражение приведения точно эквивалентно соответствующему выражению приведения в стиле C.
- Если в скобках указано более одного выражения или список инициализации в фигурных скобках (начиная с C++11) в скобках, целевой-тип должен быть классом с соответствующим образом объявленным конструктором. Это выражение является значением prvalue типа целевой-тип обозначающее временный объект (до C++17)чей объект результата (начиная с C++17) инициализируется напрямую списком-выражений.
- Если в круглых скобках нет выражения: если целевой-тип именует полный тип объекта, не являющийся массивом, это выражение является значением prvalue типа целевой-тип, обозначающее временный объект (до C++17)чей объект результата (возможно, с добавленными cv-квалификаторами) (начиная с C++17) этого типа. Если целевой-тип является типом объекта, объект инициализированный значением. Если целевой-тип (возможно, cv-квалифицированный)
void, выражение представляет собойvoidprvalue без объекта результата (начиная с C++17).
void, выражение представляет собой void prvalue без объекта результата (начиная с C++17).
Это единственное выражение приведения, которое может создать массив значений prvalue. (до C++20)auto заменяется выведенным типом придуманной переменной x, объявленной с помощью auto x(выражение); (которое никогда не интерпретируется как объявление функции) или auto x{выражение}; соответственно. Результатом всегда является prvalue объектного типа.Как и для всех выражений приведения, результатом будет:
- lvalue, если целевой-тип это ссылочный тип lvalue или ссылка rvalue на функциональный тип (начиная с C++11);
|
(начиная с 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));
}
|
Однако если самый внешний декларатор в неоднозначном операторе объявления имеет завершающий тип возвращаемого значения, этот оператор будет рассматриваться как оператор объявления только в том случае, если завершающий тип возвращаемого значения начинается с
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); // объявление объекта: нет двусмысленности для этого синтаксиса
}
|
Однако если самый внешний декларатор в неоднозначном объявлении параметра имеет завершающий тип возвращаемого значения, неоднозначность будет разрешена только путём обработки его как объявления, если оно начинается с 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; // идентификатор типа (неправильный формат)
}
|
Однако если самый внешний абстрактный декларатор в неоднозначном идентификаторе типа имеет завершающий возвращаемый тип, неоднозначность будет разрешена только путём обработки его как идентификатора типа, если он начинается с 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 по оператор приведения
| |