static_cast преобразование
Выполняет преобразование между типами с помощью сочетания неявных и пользовательских преобразований.
Синтаксис
static_cast<target-type >(expression ) |
Возвращает значение типа target-type.
Пояснение
С помощью static_cast можно выполнять только следующие преобразования, за исключением случаев, когда такие преобразования снимают константность (или volatile-квалификатор).
1) Если expression является lvalue типа «cv1Base», а target-type — «ссылка на cv2 Derived», результат ссылается на объект типа Derived, содержащий expression, если выполнены все следующие условия: -
Derivedявляется полным типом класса. -
Baseявляется базовым классомDerived. - Квалификация cv1 не выше квалификации cv2.
-
Baseявляется виртуальным базовым классомDerived. -
Baseявляется базовым классом виртуального базового классаDerived. - Не существует допустимого стандартного преобразования из «указателя на
Derived» в «указатель наBase».
Derived, поведение не определено. struct B {};
struct D : B { B b; };
D d;
B& br1 = d;
B& br2 = d.b;
static_cast<D&>(br1); // OK, lvalue denoting the original “d” object
static_cast<D&>(br2); // UB: the “b” subobject is not a base class subobject
2) Если target-type — «rvalue-ссылка на Derived», а expression является xvalue типа «(возможно, cv-квалифицированный) Base», причем Base является базовым классом Derived, результат и ограничения такого преобразования совпадают с результатом и ограничениями преобразования «lvalue типа Base в ссылку на Derived». 3) Если target-type является типом rvalue-ссылки, а тип, на который она ссылается, совместим по ссылке с типом expression, static_cast преобразует значение glvalue, class prvalue или array prvalue(до C++17)любого lvalue(начиная с C++17) expression в xvalue, ссылающееся на тот же объект, что и выражение, или на его подобъект базового класса (в зависимости от target-type).[1] Если target-type является недоступным или неоднозначным базовым классом типа expression, программа некорректна. Если expression является lvalue битового поля, сначала оно преобразуется в prvalue базового типа. | (начиная с C++11) |
| объявление Эффект такого явного преобразования совпадает с выполнением объявления и инициализации, а затем использованием | (до C++17) | ||
| выполняется любое из следующих условий:
Явное преобразование определяется следующим образом:
| (начиная с C++17) |
- преобразование lvalue в rvalue
- преобразование массива в указатель
- преобразование функции в указатель
- преобразование нулевого указателя
- преобразование нулевого указателя на член
- преобразование в bool
| (начиная с C++17) |
a) Значение типа область-перечисления можно преобразовать в целочисленный тип или тип с плавающей точкой.
| (начиная с C++11) |
- Если у target-type задан фиксированный базовый тип, expression сначала преобразуется к этому типу с помощью целочисленного продвижения или целочисленного преобразования, если это необходимо, а затем — к target-type.
- Если у target-type не задан фиксированный базовый тип, значение expression не меняется, если исходное значение входит в диапазон значений перечисления; в противном случае поведение не определено.
d) Prvalue типа с плавающей точкой можно явно преобразовать в любой другой тип с плавающей точкой.
| (начиная с C++23) |
Base» можно явно преобразовать в тип «указатель на cv2 Derived», если выполнены все следующие условия: -
Derivedявляется полным типом класса. -
Baseявляется базовым классомDerived. - Квалификация cv1 не выше квалификации cv2.
Derived, содержащий объект типа Base, на который указывает expression.-
Baseявляется виртуальным базовым классомDerived. -
Baseявляется базовым классом виртуального базового классаDerived. - Не существует допустимого стандартного преобразования из «указателя на
Derived» в «указатель наBase».
Derived, поведение не определено.Derived типа cv1 T» можно явно преобразовать в тип «указатель на член Base типа cv2 T», если выполнены все следующие условия: -
Derivedявляется полным типом класса. -
Baseявляется базовым классомDerived. - Квалификация cv1 не выше квалификации cv2.
Base.Base типа T» в «указатель на член Derived типа T», программа некорректна.Base, поведение не определено.T», если T является объектным типом, а квалификация cv1 не выше квалификации cv2.
| (до C++17) |
| (начиная с C++17) |
Как и для всех выражений приведения, результат является:
- lvalue, если target-type является типом lvalue-ссылки или rvalue-ссылкой на тип функции(начиная с C++11);
| (начиная с C++11) |
- в противном случае — prvalue.
- Этот вид static_cast используется для реализации семантики перемещения в
std::move. - Если поддерживается арифметика IEEE, по умолчанию используется округление к ближайшему.
Объекты, взаимно преобразуемые через указатель
Два объекта a и b являются взаимно преобразуемыми через указатель, если:
- они являются одним и тем же объектом; или
- один из них является объектом объединения, а другой — нестатическим членом данных этого объекта; или
- один из них является объектом класса стандартного размещения, а другой — первым нестатическим членом данных этого объекта либо любым подобъектом базового класса этого объекта; или
- существует объект
c, такой чтоaиcвзаимно преобразуемы через указатель, аcиbвзаимно преобразуемы через указатель.
union U { int a; double b; } u;
void* x = &u; // x's value is “pointer to u”
double* y = static_cast<double*>(x); // y's value is “pointer to u.b”
char* z = static_cast<char*>(x); // z's value is “pointer to u”
Примечания
Преобразования из базового класса в производный (понижающие преобразования) с помощью static_cast не выполняют проверок во время выполнения, чтобы убедиться, что динамический тип объекта, на который указывает указатель или ссылка, — Derived. Их можно безопасно использовать, только если это предварительное условие гарантируется иным способом, например при реализации статического полиморфизма. Безопасное понижающее преобразование можно выполнить с помощью dynamic_cast.
static_cast также можно использовать для устранения неоднозначности перегрузок функций, преобразовав функцию в указатель на функцию определенного типа, как показано ниже:
std::for_each(files.begin(), files.end(),
static_cast<std::ostream&(*)(std::ostream&)>(std::flush));
Ключевые слова
Пример
#include <iostream>
#include <vector>
struct B
{
int m = 42;
const char* hello() const
{
return "Hello world, this is B!\n";
}
};
struct D : B
{
const char* hello() const
{
return "Hello world, this is D!\n";
}
};
enum class E { ONE = 1, TWO, THREE };
enum EU { ONE = 1, TWO, THREE };
int main()
{
// 1. static downcast
D d;
B& br = d; // upcast via implicit conversion
std::cout << "1) " << br.hello();
D& another_d = static_cast<D&>(br); // downcast
std::cout << "1) " << another_d.hello();
// 3. lvalue to xvalue
std::vector<int> v0{1, 2, 3};
std::vector<int> v2 = static_cast<std::vector<int>&&>(v0);
std::cout << "3) after move, v0.size() = " << v0.size() << '\n';
// 4. discarded-value expression
static_cast<void>(v2.size());
// 5. initializing conversion
int n = static_cast<int>(3.14);
std::cout << "5) n = " << n << '\n';
std::vector<int> v = static_cast<std::vector<int>>(10);
std::cout << "5) v.size() = " << v.size() << '\n';
// 6. inverse of implicit conversion
void* nv = &n;
int* ni = static_cast<int*>(nv);
std::cout << "6) *ni = " << *ni << '\n';
// 7a. scoped enum to int
E e = E::TWO;
int two = static_cast<int>(e);
std::cout << "7a) " << two << '\n';
// 7b. int to enum, enum to another enum
E e2 = static_cast<E>(two);
[[maybe_unused]]
EU eu = static_cast<EU>(e2);
// 7f. pointer to member upcast
int D::*pm = &D::m;
std::cout << "7f) " << br.*static_cast<int B::*>(pm) << '\n';
// 7g. void* to any object pointer
void* voidp = &e;
[[maybe_unused]]
std::vector<int>* p = static_cast<std::vector<int>*>(voidp);
}
Вывод:
1) Hello world, this is B! 1) Hello world, this is D! 3) after move, v0.size() = 0 5) n = 3 5) v.size() = 10 6) *ni = 3 7a) 2 7f) 42
Отчеты об ошибках
Следующие отчеты об ошибках, изменяющие поведение, были ретроспективно применены к ранее опубликованным стандартам C++.
| DR | Применяется к | Опубликованное поведение | Корректное поведение |
|---|---|---|---|
| CWG 137 | C++98 | константность и volatile-квалификатор указателей на void можно было снимать | в таких случаях снимать cv-квалификации нельзя |
| CWG 427 | C++98 | понижающее преобразование могло быть неоднозначным при прямой инициализации | в этом случае выбирается понижающее преобразование |
| CWG 439 | C++98 | при преобразовании «указателя на объект» в «указатель на void», а затем обратно, его значение могло сохраняться только в том случае, если результирующий тип имел те же cv-квалификации | cv-квалификации могут отличаться |
| CWG 1094 | C++98 | преобразование значений с плавающей точкой в значения перечисления не было определено | определено |
| CWG 1320 | C++11 | преобразование значений области-перечисления в bool не было определено | определено |
| CWG 1412 | C++98 | результат преобразования из «указателя на void» в «указатель на объект» был неясен | уточнен |
| CWG 1447 | C++11 | преобразование битовых полей в rvalue-ссылки не было определено (ссылки нельзя привязать к битовым полям) | определено |
| CWG 1766 | C++98 | преобразование целочисленных значений или значений перечисления в значения перечисления давало неопределенный результат, если expression выходило за пределы диапазона | в этом случае поведение не определено |
| CWG 1832 | C++98 | при преобразовании целочисленных значений или значений перечисления в значения перечисления target-type мог быть неполным | не допускается |
| CWG 2224 | C++98 | преобразование члена базового класса в полный объект его производного класса было допустимым | в этом случае поведение не определено |
| CWG 2254 | C++11 | объект класса стандартного размещения без членов данных был взаимно преобразуем через указатель с его первым базовым классом | он взаимно преобразуем через указатель с любым из своих базовых классов |
| CWG 2284 | C++11 | объект объединения, не являющегося классом стандартного размещения, и нестатический член данных этого объекта не были взаимно преобразуемы через указатель | взаимно преобразуемы |
| CWG 2310 | C++98 | для преобразования указателя из базового класса в производный и преобразования указателя на член из производного класса в базовый тип производного класса мог быть неполным | должен быть полным |
| CWG 2338 | C++11 | преобразование в типы перечисления с фиксированным базовым типом приводило к неопределенному поведению, если expression выходило за пределы диапазона | сначала преобразовать к базовому типу (неопределенного поведения нет) |
| CWG 2499 | C++11 | класс стандартного размещения мог иметь базовый класс, не взаимно преобразуемый через указатель, даже если все подобъекты базовых классов имели один адрес | такого базового класса нет |
| CWG 2718 | C++98 | при преобразовании ссылки из базового класса в производный тип производного класса мог быть неполным | должен быть полным |
| CWG 2882 | C++98 | было неясно, пытается ли static_cast<void>(expr)сформировать последовательность неявного преобразования из expr в void | в этом случае попытка не выполняется |
Ссылки
- Стандарт C++23 (ISO/IEC 14882:2024):
- 7.6.1.9 Приведение static [expr.static.cast]
- Стандарт C++20 (ISO/IEC 14882:2020):
- 7.6.1.8 Приведение static [expr.static.cast]
- Стандарт C++17 (ISO/IEC 14882:2017):
- 8.2.9 Приведение static [expr.static.cast]
- Стандарт C++14 (ISO/IEC 14882:2014):
- 5.2.9 Приведение static [expr.static.cast]
- Стандарт C++11 (ISO/IEC 14882:2011):
- 5.2.9 Приведение static [expr.static.cast]
- Стандарт C++98 (ISO/IEC 14882:1998):
- 5.2.9 Приведение static [expr.static.cast]
- Стандарт C++03 (ISO/IEC 14882:2003):
- 5.2.9 Приведение static [expr.static.cast]
См. также
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/language/static_cast