Namespaces
Variants

static_cast 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

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

Содержание

Синтаксис

static_cast< целевой-тип  >( выражение  )

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

Объяснение

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

1) Если expression является lvalue типа " cv1 Base " и target-type является "ссылкой на cv2 Derived ", результат ссылается на объект типа Derived , содержащий expression , если выполняются все следующие условия:
  • Derived является полным типом класса.
  • Base является базовым классом Derived .
  • cv1 не является более строгой cv-квалификацией, чем cv2 .
Если выполняется любое из следующих условий, программа является некорректной:
Если expression фактически не является подобъектом базового класса объекта типа Derived , поведение не определено.
struct B {};
struct D : B { B b; };
D d;
B& br1 = d;
B& br2 = d.b;
static_cast<D&>(br1); // OK, lvalue обозначает исходный объект "d"
static_cast<D&>(br2); // UB: подобъект "b" не является подобъектом базового класса
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 является discarded-value 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 .
  • Overload resolution для direct-initialization объекта или ссылки типа target-type из expression находит хотя бы одну жизнеспособную функцию.
  • target-type является aggregate type с первым элементом x и существует неявная последовательность преобразования из expression в тип x .
(начиная с C++20)

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

  • Если target-type является ссылочным типом, эффект эквивалентен выполнению объявления и инициализации target-type temp ( expression  ) ; для некоторой вымышленной временной переменной temp с последующим использованием temp в качестве результата преобразования.
  • В противном случае, результирующий объект direct-initialized из expression .
(начиная с C++17)
6) В противном случае, если преобразование из expression в target-type является обратным стандартной последовательности преобразований, и последовательность преобразований не содержит ни одного из следующих преобразований, преобразование может быть выполнено с помощью static_cast :
(since C++17)
Если программа использует static_cast для выполнения обратного преобразования некорректной стандартной последовательности преобразований, она является некорректной.
7) В противном случае к expression применяются преобразования lvalue-to-rvalue, array-to-pointer и function-to-pointer. После этих преобразований только следующие конверсии могут быть выполнены с помощью static_cast :
a) Значение типа scoped enumeration может быть преобразовано в целочисленный или тип с плавающей точкой.
  • Если 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 сначала преобразуется в этот тип с помощью integral promotion или integral conversion , если необходимо, а затем в target-type .
  • Если target-type не имеет фиксированного базового типа, значение expression остаётся неизменным, если исходное значение находится в диапазоне значений перечисления , в противном случае поведение не определено.
c) Значение типа с плавающей запятой также может быть преобразовано в любой полный тип перечисления. Результат аналогичен преобразованию исходного значения expression сначала в базовый тип target-type , а затем в сам target-type .
d) Правостое значение типа с плавающей запятой может быть явно преобразовано в любой другой тип с плавающей запятой.
  • Если исходное значение expression может быть точно представлено в target-type , оно не изменяется.
  • В противном случае, если исходное значение expression находится между двумя представимыми значениями target-type , результат преобразования является определяемым реализацией выбором одного из этих значений. [2]
  • В противном случае поведение не определено.
(since C++23)
e) Значение rvalue (до C++11) Значение prvalue (начиная с C++11) типа «указатель на cv1 Base » может быть явно преобразовано в тип «указатель на cv2 Derived », если выполняются все следующие условия:
  • Derived является полным типом класса.
  • Base является базовым классом для Derived .
  • cv1 не является более строгой cv-квалификацией, чем 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 не является более строгой cv-квалификацией, чем cv2 .
Если expression является нулевым значением указателя на член, результатом будет нулевое значение указателя на член типа target-type . В противном случае результатом будет указатель на исходный (возможно, косвенный) член класса Base .
Если не существует допустимого стандартного преобразования из "указатель на член Base типа T " в "указатель на член Derived типа T ", программа является некорректной.
Если expression не является нулевым значением указателя на член и член, который он обозначает, не является (возможно, косвенным) членом класса Base , поведение не определено.
g) Значение rvalue (до C++11) Значение prvalue (начиная с C++11) типа "указатель на cv1 void " может быть явно преобразовано в тип "указатель на cv2 T ", если T является объектным типом и cv1 не является более строгой cv-квалификацией, чем cv2 .
  • Если выражение является нулевым указателем, результатом будет нулевой указатель типа целевой-тип .
  • Если выражение представляет адрес A байта в памяти и A удовлетворяет требованию выравнивания для T , то результирующее значение указателя также представляет A .
  • Результат любого другого такого преобразования указателя не определён.
  • Если выражение является результатом предыдущего преобразования из объекта типа "указатель на cv3 T ", результат имеет исходное значение.
(до C++17)
  • Если выражение представляет адрес A байта в памяти , но A не удовлетворяет требованию выравнивания для T , то результирующее значение указателя не определено.
  • Иначе, если выражение указывает на объект a , и существует объект b типа T (игнорируя cv-квалификацию), который является pointer-interconvertible (см. ниже) с a , результатом будет указатель на b .
  • Иначе, значение указателя не изменяется при преобразовании.
(начиная с C++17)

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

  • lvalue, если target-type является типом lvalue-ссылки или rvalue-ссылкой на тип функции (начиная с C++11) ;
  • xvalue, если target-type является rvalue-ссылкой на объектный тип;
(since C++11)
  • в противном случае является prvalue.
  1. Этот тип static_cast используется для реализации семантики перемещения в std::move .
  2. Если поддерживается арифметика IEEE, округление по умолчанию происходит к ближайшему значению.

Указательно-конвертируемые объекты

Два объекта a и b являются pointer-interconvertible если:

  • они являются одним и тем же объектом, или
  • один является объектом объединения, а другой - нестатическим элементом данных этого объекта, или
  • один является стандартно-компонуемым объектом класса, а другой - первым нестатическим элементом данных этого объекта или любого подобъекта базового класса этого объекта, или
  • существует объект c такой, что a и c являются pointer-interconvertible, и c и b являются pointer-interconvertible.
union U { int a; double b; } u;
void* x = &u;                        // значение x — «указатель на u»
double* y = static_cast<double*>(x); // значение y — «указатель на u.b»
char* z = static_cast<char*>(x);     // значение z — «указатель на 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. статическое приведение вниз
    D d;
    B& br = d; // приведение вверх через неявное преобразование
    std::cout << "1) " << br.hello();
    D& another_d = static_cast<D&>(br); // приведение вниз
    std::cout << "1) " << another_d.hello();
    // 3. lvalue в xvalue
    std::vector<int> v0{1, 2, 3};
    std::vector<int> v2 = static_cast<std::vector<int>&&>(v0);
    std::cout << "3) после перемещения, v0.size() = " << v0.size() << '\n';
    // 4. выражение с отбрасываемым значением
    static_cast<void>(v2.size());
    // 5. инициализирующее преобразование
    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. обратное неявному преобразованию
    void* nv = &n;
    int* ni = static_cast<int*>(nv);
    std::cout << "6) *ni = " << *ni << '\n';
    // 7a. scoped enum в int
    E e = E::TWO;
    int two = static_cast<int>(e);
    std::cout << "7a) " << two << '\n';
    // 7b. int в enum, enum в другой enum
    E e2 = static_cast<E>(two);
    [[maybe_unused]]
    EU eu = static_cast<EU>(e2);
    // 7f. указатель на член - приведение вверх
    int D::*pm = &D::m;
    std::cout << "7f) " << br.*static_cast<int B::*>(pm) << '\n';
    // 7g. void* в любой указатель на объект
    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
Перевод текста на веб-странице: 1) Привет мир, это B! 1) Привет мир, это D! 3) после перемещения, v0.size() = 0 5) n = 3 5) v.size() = 10 6) *ni = 3 7a) 2 7f) 42 *Примечание: HTML-теги, атрибуты и C++ специфические термины (size(), move, n, v, ni) сохранены без изменений в соответствии с требованиями.*

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

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

DR Применяется к Поведение в опубликованной версии Корректное поведение
CWG 137 C++98 константность и волатильность указателей на
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 объект нестандартной компоновки union и нестатический член данных
этого объекта не были указательно-взаимопреобразуемы
они являются
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 Статическое приведение [expr.static.cast]
  • Стандарт C++20 (ISO/IEC 14882:2020):
  • 7.6.1.8 Статическое приведение [expr.static.cast]
  • Стандарт C++17 (ISO/IEC 14882:2017):
  • 8.2.9 Статическое приведение [expr.static.cast]
  • Стандарт C++14 (ISO/IEC 14882:2014):
  • 5.2.9 Статическое приведение [expr.static.cast]
  • Стандарт C++11 (ISO/IEC 14882:2011):
  • 5.2.9 Статическое приведение [expr.static.cast]
  • Стандарт C++98 (ISO/IEC 14882:1998):
  • 5.2.9 Статическое приведение [expr.static.cast]
  • Стандарт C++03 (ISO/IEC 14882:2003):
  • 5.2.9 Статическое приведение [expr.static.cast]

Смотрите также