Namespaces
Variants
Views
Actions

Difference between revisions of "cpp/language/static cast"

From cppreference.com
< cpp‎ | language
m (Example: += [[maybe_unused]])
(Explanation: {{c}} for static_cast, CWG1766)
Line 12: Line 12:
  
 
===Explanation===
 
===Explanation===
 
 
Only the following conversions can be done with {{c|static_cast}}, except when such conversions would cast away ''constness'' or ''volatility''.
 
Only the following conversions can be done with {{c|static_cast}}, except when such conversions would cast away ''constness'' or ''volatility''.
  
@1@ If there is an {{rlp|implicit cast|implicit conversion sequence}} from {{spar|expression}} to {{spar|new_type}}, or if overload resolution for a {{rlp|direct initialization}} of an object or reference of type {{spar|new_type}} from {{spar|expression}} would find at least one viable function, then {{c|static_cast<new_type>(expression)}} returns the imaginary variable {{tt|Temp}} initialized as if by {{c|new_type Temp(expression);}}, which may involve {{rlp|implicit cast|implicit conversions}}, a call to the {{rlp|constructor}} of {{spar|new_type}} or a call to a {{rlp|cast operator|user-defined conversion operator}}. {{rev inl|since=c++17|For non-reference {{spar|new_type}}, the result object of the {{tt|static_cast}} prvalue expression is what's direct-initialized.}}
+
@1@ If there is an {{rlp|implicit conversion|implicit conversion sequence}} from {{spar|expression}} to {{spar|new_type}}, or if overload resolution for a {{rlp|direct initialization}} of an object or reference of type {{spar|new_type}} from {{spar|expression}} would find at least one viable function, then {{c|static_cast<new_type>(expression)}} returns the imaginary variable {{tt|Temp}} initialized as if by {{c|new_type Temp(expression);}}, which may involve {{rlp|implicit conversion}}s, a call to the {{rlp|constructor}} of {{spar|new_type}} or a call to a {{rlp|cast operator|user-defined conversion operator}}. {{rev inl|since=c++17|For non-reference {{spar|new_type}}, the result object of the {{c|static_cast}} prvalue expression is what's direct-initialized.}}
@2@ If {{spar|new_type}} is a reference or pointer to some class {{tt|D}} and  {{spar|expression}} is lvalue of its non-virtual base {{tt|B}} or prvalue pointer to it, {{tt|static_cast}} performs a ''downcast''. (This downcast is ill-formed if {{tt|B}} is ambiguous, inaccessible, or virtual base (or a base of a virtual base) of {{tt|D}}.) Such a downcast makes no runtime checks to ensure that the object's runtime type is actually {{tt|D}}, and may only be used safely if this precondition is guaranteed by other means, such as when implementing [[enwiki:Curiously recurring template pattern#Static polymorphism|static polymorphism]]. Safe downcast may be done with {{ltt|cpp/language/dynamic_cast}}.
+
@2@ If {{spar|new_type}} is a reference or pointer to some class {{tt|D}} and  {{spar|expression}} is lvalue of its non-virtual base {{tt|B}} or prvalue pointer to it, {{c|static_cast}} performs a ''downcast''. (This downcast is ill-formed if {{tt|B}} is ambiguous, inaccessible, or virtual base (or a base of a virtual base) of {{tt|D}}.) Such a downcast makes no runtime checks to ensure that the object's runtime type is actually {{tt|D}}, and may only be used safely if this precondition is guaranteed by other means, such as when implementing [[enwiki:Curiously recurring template pattern#Static polymorphism|static polymorphism]]. Safe downcast may be done with {{rlpt|dynamic_cast}}.
 
{{source|1=struct B { };
 
{{source|1=struct B { };
 
struct D : B { };
 
struct D : B { };
Line 24: Line 23:
 
}}
 
}}
  
{{rev begin}}
+
{{rrev|since=c++11|
{{rev|since=c++11|
+
@3@ If {{spar|new_type}} is an rvalue reference type, {{c|static_cast}} converts the value of {{rev inl|until=c++17|glvalue, class prvalue, or array prvalue}}{{rev inl|since=c++17|any lvalue}} {{spar|expression}} to ''xvalue'' referring to the same object as the expression, or to its base sub-object (depending on {{spar|new_type}}). If the target type is an inaccessible or ambiguous base of the type of the expression, the program is ill-formed. If the expression is a {{rlp|bit field}} lvalue, it is first converted to prvalue of the underlying type. This type of {{c|static_cast}} is used to implement move semantics in {{lc|std::move}}.
@3@ If {{spar|new_type}} is an rvalue reference type, {{tt|static_cast}} converts the value of {{rev inl|until=c++17|glvalue, class prvalue, or array prvalue}}{{rev inl|since=c++17|any lvalue}} {{spar|expression}} to ''xvalue'' referring to the same object as the expression, or to its base sub-object (depending on {{spar|new_type}}). If the target type is an inaccessible or ambiguous base of the type of the expression, the program is ill-formed. If the expression is a {{rlp|bit field}} lvalue, it is first converted to prvalue of the underlying type. This type of {{tt|static_cast}} is used to implement move semantics in {{lc|std::move}}.
+
 
}}
 
}}
{{rev end}}
+
@4@ If {{spar|new_type}} is the type {{c|void}} (possibly cv-qualified), {{c|static_cast}} discards the value of {{spar|expression}} after evaluating it.
@4@ If {{spar|new_type}} is the type {{c|void}} (possibly cv-qualified), {{tt|static_cast}} discards the value of {{spar|expression}} after evaluating it.
+
@5@ If a {{rlp|implicit_conversion|standard conversion}} sequence from {{spar|new_type}} to the type of {{spar|expression}} exists, that does not include lvalue-to-rvalue, array-to-pointer, function-to-pointer, null pointer, null member pointer, {{rev inl|since=c++17|function pointer,}} or boolean conversion, then {{c|static_cast}} can perform the inverse of that implicit conversion.
@5@ If a {{rlp|implicit_cast |standard conversion}} sequence from {{spar|new_type}} to the type of {{spar|expression}} exists, that does not include lvalue-to-rvalue, array-to-pointer, function-to-pointer, null pointer, null member pointer, {{rev inl|since=c++17|function pointer,}} or boolean conversion, then {{tt|static_cast}} can perform the inverse of that implicit conversion.
+
@6@ If conversion of {{spar|expression}} to {{spar|new_type}} involves lvalue-to-rvalue, array-to-pointer, or function-to-pointer conversion, it can be performed explicitly by {{c|static_cast}}.
@6@ If conversion of {{spar|expression}} to {{spar|new_type}} involves lvalue-to-rvalue, array-to-pointer, or function-to-pointer conversion, it can be performed explicitly by {{tt|static_cast}}.
+
 
@7@ {{rlp|enum|Scoped enumeration}}{{mark c++11}} type can be converted to an integer or floating-point type.
 
@7@ {{rlp|enum|Scoped enumeration}}{{mark c++11}} type can be converted to an integer or floating-point type.
{{rev begin}}
+
{{rrev multi|rev1=
{{rev|until=c++20|
+
When the target type is {{c|bool}} (possibly cv-qualified), the result is {{c|false}} if the original value is zero and {{c|true}} for all other values. For the remaining integral types, the result is the value of the enum if it can be represented by the target type and unspecified otherwise.
When the target type is {{c|bool}} (possibly cv-qualified), the result is {{c|false}} if the original value is zero and {{c|true}} for all other values. For the remaining integral types, the result is the value of the enum if it can be represented by the target type and unspecified otherwise
+
|since2=c++20|rev2=
}}
+
{{rev|since=c++20|
+
 
The result is the same as {{rlp|implicit conversion}} from the enum's underlying type to the destination type.
 
The result is the same as {{rlp|implicit conversion}} from the enum's underlying type to the destination type.
 
}}
 
}}
{{rev end}}
 
 
@8@ A value of integer or enumeration type can be converted to any complete {{rlp|enum|enumeration type}}.
 
@8@ A value of integer or enumeration type can be converted to any complete {{rlp|enum|enumeration type}}.
:* If the underlying type is not fixed, the result is {{rev inl|until=c++17|unspecified}}{{rev inl|since=c++17|undefined behavior}} if the value of {{spar|expression}} is out of range (the range is all values possible for the smallest bit field large enough to hold all enumerators of the target enumeration).
+
:* If the underlying type is not fixed, the behavior is undefined if the value of {{spar|expression}} is out of range (the range is all values possible for the smallest bit field large enough to hold all enumerators of the target enumeration).<!-- CWG 1766 -->
:* <!--cwg 2338--> If the underlying type is fixed, the result is the same as {{rlp|implicit_cast#Integral_conversions|converting}} the original value first to the underlying type of the enumeration and then to the enumeration type.<!-- end of cwg 2338 -->
+
:* <!--cwg 2338--> If the underlying type is fixed, the result is the same as {{rlp|implicit_conversion#Integral_conversions|converting}} the original value first to the underlying type of the enumeration and then to the enumeration type.<!-- end of cwg 2338 -->
 
@@ A value of a floating-point type can also be converted to any complete enumeration type.
 
@@ A value of a floating-point type can also be converted to any complete enumeration type.
:* The result is the same as {{rlp|implicit_cast#Floating–integral conversions|converting}} the original value first to the underlying type of the enumeration, and then to the enumeration type.
+
:* The result is the same as {{rlp|implicit_conversion#Floating–integral conversions|converting}} the original value first to the underlying type of the enumeration, and then to the enumeration type.
@9@ A pointer to member of some class {{tt|D}} can be upcast to a pointer to member of its unambiguous, accessible base class {{tt|B}}. This {{tt|static_cast}} makes no checks to ensure the member actually exists in the runtime type of the pointed-to object.
+
@9@ A pointer to member of some class {{tt|D}} can be upcast to a pointer to member of its unambiguous, accessible base class {{tt|B}}. This {{c|static_cast}} makes no checks to ensure the member actually exists in the runtime type of the pointed-to object.
 
@10@ A prvalue of type pointer to {{c|void}} (possibly cv-qualified) can be converted to pointer to any object type. If the original {{rlp|pointer#Pointers|pointer value}} represents an address of a byte in memory that does not satisfy the alignment requirement of the target type, then the resulting pointer value is unspecified. Otherwise, if the original pointer value points to an object ''a'', and there is an object ''b'' of the target type (ignoring cv-qualification) that is ''pointer-interconvertible'' (as defined below) with ''a'', the result is a pointer to ''b''. Otherwise the pointer value is unchanged. Conversion of any pointer to pointer to void and back to pointer to the original (or more cv-qualified) type preserves its original value.
 
@10@ A prvalue of type pointer to {{c|void}} (possibly cv-qualified) can be converted to pointer to any object type. If the original {{rlp|pointer#Pointers|pointer value}} represents an address of a byte in memory that does not satisfy the alignment requirement of the target type, then the resulting pointer value is unspecified. Otherwise, if the original pointer value points to an object ''a'', and there is an object ''b'' of the target type (ignoring cv-qualification) that is ''pointer-interconvertible'' (as defined below) with ''a'', the result is a pointer to ''b''. Otherwise the pointer value is unchanged. Conversion of any pointer to pointer to void and back to pointer to the original (or more cv-qualified) type preserves its original value.
  

Revision as of 22:25, 16 December 2021

 
 
C++ language
General topics
Flow control
Conditional execution statements
if
Iteration statements (loops)
for
range-for (C++11)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications (until C++17*)
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
const/volatile
decltype (C++11)
auto (C++11)
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
explicit (C++11)
static

Special member functions
Templates
Miscellaneous
 
 

Converts between types using a combination of implicit and user-defined conversions.

Contents

Syntax

static_cast < new_type > ( expression )

Returns a value of type new_type.

Explanation

Only the following conversions can be done with static_cast, except when such conversions would cast away constness or volatility.

1) If there is an implicit conversion sequence from expression to new_type, or if overload resolution for a direct initialization of an object or reference of type new_type from expression would find at least one viable function, then static_cast<new_type>(expression) returns the imaginary variable Temp initialized as if by new_type Temp(expression);, which may involve implicit conversions, a call to the constructor of new_type or a call to a user-defined conversion operator. For non-reference new_type, the result object of the static_cast prvalue expression is what's direct-initialized.(since C++17)
2) If new_type is a reference or pointer to some class D and expression is lvalue of its non-virtual base B or prvalue pointer to it, static_cast performs a downcast. (This downcast is ill-formed if B is ambiguous, inaccessible, or virtual base (or a base of a virtual base) of D.) Such a downcast makes no runtime checks to ensure that the object's runtime type is actually D, and may only be used safely if this precondition is guaranteed by other means, such as when implementing static polymorphism. Safe downcast may be done with dynamic_cast.
struct B { };
struct D : B { };
D d;
B& br = d;
static_cast<D&>(br); // lvalue denoting the original d object
3) If new_type is an rvalue reference type, static_cast converts the value of glvalue, class prvalue, or array prvalue(until C++17)any lvalue(since C++17) expression to xvalue referring to the same object as the expression, or to its base sub-object (depending on new_type). If the target type is an inaccessible or ambiguous base of the type of the expression, the program is ill-formed. If the expression is a bit field lvalue, it is first converted to prvalue of the underlying type. This type of static_cast is used to implement move semantics in std::move.
(since C++11)
4) If new_type is the type void (possibly cv-qualified), static_cast discards the value of expression after evaluating it.
5) If a standard conversion sequence from new_type to the type of expression exists, that does not include lvalue-to-rvalue, array-to-pointer, function-to-pointer, null pointer, null member pointer, function pointer,(since C++17) or boolean conversion, then static_cast can perform the inverse of that implicit conversion.
6) If conversion of expression to new_type involves lvalue-to-rvalue, array-to-pointer, or function-to-pointer conversion, it can be performed explicitly by static_cast.
7) Scoped enumeration(C++11) type can be converted to an integer or floating-point type.

When the target type is bool (possibly cv-qualified), the result is false if the original value is zero and true for all other values. For the remaining integral types, the result is the value of the enum if it can be represented by the target type and unspecified otherwise.

(until C++20)

The result is the same as implicit conversion from the enum's underlying type to the destination type.

(since C++20)
8) A value of integer or enumeration type can be converted to any complete enumeration type.
  • If the underlying type is not fixed, the behavior is undefined if the value of expression is out of range (the range is all values possible for the smallest bit field large enough to hold all enumerators of the target enumeration).
  • If the underlying type is fixed, the result is the same as converting the original value first to the underlying type of the enumeration and then to the enumeration type.
A value of a floating-point type can also be converted to any complete enumeration type.
  • The result is the same as converting the original value first to the underlying type of the enumeration, and then to the enumeration type.
9) A pointer to member of some class D can be upcast to a pointer to member of its unambiguous, accessible base class B. This static_cast makes no checks to ensure the member actually exists in the runtime type of the pointed-to object.
10) A prvalue of type pointer to void (possibly cv-qualified) can be converted to pointer to any object type. If the original pointer value represents an address of a byte in memory that does not satisfy the alignment requirement of the target type, then the resulting pointer value is unspecified. Otherwise, if the original pointer value points to an object a, and there is an object b of the target type (ignoring cv-qualification) that is pointer-interconvertible (as defined below) with a, the result is a pointer to b. Otherwise the pointer value is unchanged. Conversion of any pointer to pointer to void and back to pointer to the original (or more cv-qualified) type preserves its original value.

As with all cast expressions, the result is:

  • an lvalue if target-type is an lvalue reference type or an rvalue reference to function type(since C++11);
  • an xvalue if target-type is an rvalue reference to object type;
(since C++11)
  • a prvalue otherwise.

Two objects a and b are pointer-interconvertible if:

  • they are the same object, or
  • one is a union object and the other is a non-static data member of that object, or
  • one is a standard-layout class object and the other is the first non-static data member of that object, or, if the object has no non-static data members, any base class subobject of that object, or
  • there exists an object c such that a and c are pointer-interconvertible, and c and b are pointer-interconvertible.
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"

Notes

static_cast may also be used to disambiguate function overloads by performing a function-to-pointer conversion to specific type, as in

std::for_each(files.begin(), files.end(),
              static_cast<std::ostream&(*)(std::ostream&)>(std::flush));

Keywords

static_cast

Example

#include <vector>
#include <iostream>
 
struct B {
    int m = 0;
    void hello() const {
        std::cout << "Hello world, this is B!\n";
    }
};
struct D : B {
    void hello() const {
        std::cout << "Hello world, this is D!\n";
    }
};
 
enum class E { ONE = 1, TWO, THREE };
enum EU { ONE = 1, TWO, THREE };
 
int main()
{
    // 1: initializing conversion
    int n = static_cast<int>(3.14); 
    std::cout << "n = " << n << '\n';
    std::vector<int> v = static_cast<std::vector<int>>(10);
    std::cout << "v.size() = " << v.size() << '\n';
 
    // 2: static downcast
    D d;
    B& br = d; // upcast via implicit conversion
    br.hello();
    D& another_d = static_cast<D&>(br); // downcast
    another_d.hello();
 
    // 3: lvalue to xvalue
    std::vector<int> v2 = static_cast<std::vector<int>&&>(v);
    std::cout << "after move, v.size() = " << v.size() << '\n';
 
    // 4: discarded-value expression
    static_cast<void>(v2.size());
 
    // 5. inverse of implicit conversion
    void* nv = &n;
    int* ni = static_cast<int*>(nv);
    std::cout << "*ni = " << *ni << '\n';
 
    // 6. array-to-pointer followed by upcast
    D a[10];
    [[maybe_unused]]
    B* dp = static_cast<B*>(a);
 
    // 7. scoped enum to int or float
    E e = E::ONE;
    int one = static_cast<int>(e);
    std::cout << one << '\n';
 
    // 8. int to enum, enum to another enum
    E e2 = static_cast<E>(one);
    [[maybe_unused]]
    EU eu = static_cast<EU>(e2);
 
    // 9. pointer to member upcast
    int D::*pm = &D::m;
    std::cout << br.*static_cast<int B::*>(pm) << '\n';
 
    // 10. void* to any type
    void* voidp = &e;
    [[maybe_unused]]
    std::vector<int>* p = static_cast<std::vector<int>*>(voidp);
}

Output:

n = 3
v.size() = 10
Hello world, this is B!
Hello world, this is D!
after move, v.size() = 0
*ni = 3
1
0

Defect reports

The following behavior-changing defect reports were applied retroactively to previously published C++ standards.

DR Applied to Behavior as published Correct behavior
CWG 2254 C++11 standard-layout class object with no data members

is pointer-interconvertible to its first base class

is pointer-interconvertible

to any of its base classes

See also