Spec-Zone.ru › C++

static_cast преобразование

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

Синтаксис

static_cast<target-type >(expression )

Возвращает значение типа target-type.

Пояснение

С помощью static_cast можно выполнять только следующие преобразования, за исключением случаев, когда такие преобразования снимают константность (или volatile-квалификатор).

1) Если expression является lvalue типа «cv1 Base», а target-type — «ссылка на cv2 Derived», результат ссылается на объект типа Derived, содержащий expression, если выполнены все следующие условия:
  • Derived является полным типом класса.
  • Base является базовым классом Derived.
  • Квалификация cv1 не выше квалификации cv2.
Если выполнено любое из следующих условий, программа некорректна:
  • Base является виртуальным базовым классом Derived.
  • Base является базовым классом виртуального базового класса Derived.
  • Не существует допустимого стандартного преобразования из «указателя на Derived» в «указатель на Base».
Если expression фактически не является подобъектом базового класса объекта типа 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)
4) Если target-type — (возможно, cv-квалифицированный) void, преобразование не дает результата. В этом случае expression является выражением с отбрасываемым значением.
5) В противном случае expression можно явно преобразовать к target-type, если

объявление target-type temp(expression ); корректно для некоторой условной временной переменной temp.

Эффект такого явного преобразования совпадает с выполнением объявления и инициализации, а затем использованием temp в качестве результата преобразования. expression  используется как lvalue(до C++11)glvalue(начиная с C++11) тогда и только тогда, когда при инициализации оно используется как lvalue(до C++11)glvalue(начиная с C++11).

(до C++17)

выполняется любое из следующих условий:

  • Существует последовательность неявного преобразования из expression в target-type.
  • Разрешение перегрузки для прямой инициализации объекта или ссылки типа target-type из expression нашло бы как минимум одну подходящую функцию.
  • target-type является агрегатным типом с первым элементом x, и существует последовательность неявного преобразования из expression в тип x.
(начиная с C++20)

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

  • Если target-type является ссылочным типом, эффект совпадает с выполнением объявления и инициализации target-type temp(expression ); для некоторой условной временной переменной temp, а затем использованием temp в качестве результата преобразования.
  • В противном случае объект результата напрямую инициализируется из expression .
(начиная с C++17)
6) В противном случае, если преобразование из expression в target-type является обратным к последовательности стандартных преобразований и эта последовательность не содержит ни одного из следующих преобразований, преобразование можно выполнить с помощью static_cast:
  • преобразование lvalue в rvalue
  • преобразование массива в указатель
  • преобразование функции в указатель
  • преобразование нулевого указателя
  • преобразование нулевого указателя на член
  • преобразование в bool
  • преобразование указателя на функцию
(начиная с C++17)
Если программа использует static_cast для выполнения обратного преобразования к некорректной последовательности стандартных преобразований, она некорректна.
7) В противном случае к expression применяются преобразования lvalue в rvalue, массива в указатель и функции в указатель. После этих преобразований с помощью static_cast можно выполнить только следующие преобразования:
a) Значение типа область-перечисления можно преобразовать в целочисленный тип или тип с плавающей точкой.
  • Если target-type — (возможно, cv-квалифицированный) bool, результат равен false, если исходное значение expression равно нулю, и true для всех остальных значений.
  • Если target-type — целочисленный тип, отличный от (возможно, cv-квалифицированного) bool, значение не меняется, если исходное значение expression представимо типом target-type. В противном случае результирующее значение не определено.
(до C++20)
  • Если target-type является целочисленным типом, результат совпадает с результатом преобразования к базовому типу перечисления, а затем к target-type.
(начиная с C++20)
  • Если target-type является типом с плавающей точкой, результат совпадает с результатом преобразования исходного значения к target-type.
(начиная с C++11)
b) Значение целочисленного типа или типа перечисления можно преобразовать в любой полный тип перечисления.
  • Если у target-type задан фиксированный базовый тип, expression сначала преобразуется к этому типу с помощью целочисленного продвижения или целочисленного преобразования, если это необходимо, а затем — к target-type.
  • Если у target-type не задан фиксированный базовый тип, значение expression не меняется, если исходное значение входит в диапазон значений перечисления; в противном случае поведение не определено.
c) Значение типа с плавающей точкой также можно преобразовать в любой полный тип перечисления. Результат совпадает с результатом преобразования исходного значения expression сначала к базовому типу target-type, а затем к самому target-type.
d) Prvalue типа с плавающей точкой можно явно преобразовать в любой другой тип с плавающей точкой.
  • Если исходное значение expression точно представимо в target-type, оно не изменяется.
  • В противном случае, если исходное значение expression находится между двумя представимыми значениями target-type, результат преобразования выбирается реализацией из этих двух значений.[2]
  • В противном случае поведение не определено.
(начиная с C++23)
e) Rvalue(до C++11)Prvalue(начиная с C++11) типа «указатель на cv1 Base» можно явно преобразовать в тип «указатель на cv2 Derived», если выполнены все следующие условия:
  • Derived является полным типом класса.
  • Base является базовым классом Derived.
  • Квалификация cv1 не выше квалификации cv2.
Если expression является нулевым указателем, результатом будет нулевой указатель типа target-type. В противном случае результатом будет указатель на объект типа Derived, содержащий объект типа Base, на который указывает expression.
Если выполнено любое из следующих условий, программа некорректна:
  • Base является виртуальным базовым классом Derived.
  • Base является базовым классом виртуального базового класса Derived.
  • Не существует допустимого стандартного преобразования из «указателя на Derived» в «указатель на Base».
Если expression не является нулевым указателем и фактически не указывает на подобъект базового класса объекта типа Derived, поведение не определено.
f) Rvalue(до C++11)Prvalue(начиная с C++11) типа «указатель на член Derived типа cv1 T» можно явно преобразовать в тип «указатель на член Base типа cv2 T», если выполнены все следующие условия:
  • Derived является полным типом класса.
  • Base является базовым классом Derived.
  • Квалификация cv1 не выше квалификации cv2.
Если expression является нулевым указателем на член, результатом будет нулевой указатель на член типа target-type. В противном случае результатом будет указатель на исходный (возможно, косвенный) член класса Base.
Если не существует допустимого стандартного преобразования из «указателя на член Base типа T» в «указатель на член Derived типа T», программа некорректна.
Если expression не является нулевым указателем на член и обозначаемый им член не является (возможно, косвенным) членом класса Base, поведение не определено.
g) Rvalue(до C++11)Prvalue(начиная с C++11) типа «указатель на cv1 void» можно явно преобразовать в тип «указатель на cv2 T», если T является объектным типом, а квалификация cv1 не выше квалификации cv2.
  • Если expression является нулевым указателем, результатом будет нулевой указатель типа target-type.
  • Если expression представляет адрес A байта в памяти, а A удовлетворяет требованию выравнивания для T, результирующее значение указателя также представляет A.
  • Результат любого другого такого преобразования указателя не определен.
  • Если expression является результатом предыдущего преобразования из объекта типа «указатель на cv3 T», результат имеет исходное значение.
(до C++17)
  • Если expression представляет адрес A байта в памяти, но A не удовлетворяет требованию выравнивания для T, результирующее значение указателя не определено.
  • В противном случае, если expression указывает на объект a и существует объект b типа T (без учета cv-квалификации), который является взаимно преобразуемым через указатель (см. ниже) с a, результатом будет указатель на b.
  • В противном случае значение указателя не меняется при преобразовании.
(начиная с C++17)

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

  • lvalue, если target-type является типом lvalue-ссылки или rvalue-ссылкой на тип функции(начиная с C++11);
  • xvalue, если target-type является rvalue-ссылкой на объектный тип;
(начиная с C++11)
  • в противном случае — prvalue.
  1. Этот вид static_cast используется для реализации семантики перемещения в std::move.
  2. Если поддерживается арифметика 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));

Ключевые слова

static_cast

Пример

#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]

См. также

  • const_cast
  • dynamic_cast
  • reinterpret_cast
  • явное приведение
  • неявные преобразования

© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/language/static_cast

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API