Difference between revisions of "cpp/language/explicit cast"
m (Minor fix.) |
Andreas Krug (Talk | contribs) m (an prvalue -> a prvalue, a expression -> an expression) |
||
Line 30: | Line 30: | ||
* If there is exactly one expression in parentheses, this cast expression is exactly equivalent to the corresponding C-style cast expression. | * If there is exactly one expression in parentheses, this cast expression is exactly equivalent to the corresponding C-style cast expression. | ||
* If there are more than one expression {{rev inl|since=c++11|or {{rlp|list initialization|braced-init-list}}}} in parentheses, {{spar|new-type}} must be a class with a suitably declared {{rlp|constructor}}. This expression is a prvalue of type {{spar|new-type}} {{rev inl|until=c++17|designating a temporary}}{{rev inl|since=c++17|whose result object is}} {{rlp|direct initialization|direct-initialized}} with {{spar|expression-list}}. | * If there are more than one expression {{rev inl|since=c++11|or {{rlp|list initialization|braced-init-list}}}} in parentheses, {{spar|new-type}} must be a class with a suitably declared {{rlp|constructor}}. This expression is a prvalue of type {{spar|new-type}} {{rev inl|until=c++17|designating a temporary}}{{rev inl|since=c++17|whose result object is}} {{rlp|direct initialization|direct-initialized}} with {{spar|expression-list}}. | ||
− | * If there's no expression in parentheses: if {{spar|new-type}} names a non-array complete object type, this expression is | + | * If there's no expression in parentheses: if {{spar|new-type}} names a non-array complete object type, this expression is a prvalue of type {{spar|new-type}}, {{rev inl|until=c++17|designating a temporary}}{{rev inl|since=c++17|whose result object is (possibly with added cv-qualifiers)}} of that type. If {{spar|new-type}} is an object type, the object is {{rlp|value initialization|value-initialized}}. If {{spar|new-type}} is (possibly {{rlp|cv|cv-qualified}}) {{c|void}}, the expression is a {{c|void}} prvalue {{rev inl|since=c++17|without a result object}}. |
@3@ A single-word type name followed by a ''braced-init-list'' is a prvalue of the specified type {{rev inl|until=c++17|designating a temporary}}{{rev inl|since=c++17|whose result object is}} {{rlp|list initialization|direct-list-initialized}} with the specified ''braced-init-list''. If {{spar|new-type}} is (possibly {{rlp|cv|cv-qualified}}) {{c|void}}, the expression is a {{c|void}} prvalue {{rev inl|since=c++17|without a result object}}. {{rev inl|until=c++20| | @3@ A single-word type name followed by a ''braced-init-list'' is a prvalue of the specified type {{rev inl|until=c++17|designating a temporary}}{{rev inl|since=c++17|whose result object is}} {{rlp|list initialization|direct-list-initialized}} with the specified ''braced-init-list''. If {{spar|new-type}} is (possibly {{rlp|cv|cv-qualified}}) {{c|void}}, the expression is a {{c|void}} prvalue {{rev inl|since=c++17|without a result object}}. {{rev inl|until=c++20| | ||
Line 42: | Line 42: | ||
===Ambiguity Resolution=== | ===Ambiguity Resolution=== | ||
− | In the case of an ambiguity between | + | In the case of an ambiguity between an expression statement with a function-style cast expression as its leftmost subexpression and a declaration statement, the ambiguity is resolved by treating it as a declaration. This disambiguation is purely syntactic: it does not consider the meaning of names occurring in the statement other than whether they are type names: |
{{source|1= | {{source|1= | ||
struct M {}; | struct M {}; |
Revision as of 03:16, 8 January 2023
Converts between types using a combination of explicit and implicit conversions.
Contents |
Syntax
( new-type ) expression
|
(1) | ||||||||
new-type ( expression-list(optional) )
|
(2) | ||||||||
new-type { expression-list(optional) }
|
(3) | (since C++11) | |||||||
template-name ( expression-list(optional) )
|
(4) | (since C++17) | |||||||
template-name { expression-list(optional) }
|
(5) | (since C++17) | |||||||
auto ( expression )
|
(6) | (since C++23) | |||||||
auto { expression }
|
(7) | (since C++23) | |||||||
Returns a value of type new-type.
Explanation
const_cast<
new-type>(
expression)
;static_cast<
new-type>(
expression)
, with extensions: pointer or reference to a derived class is additionally allowed to be cast to pointer or reference to unambiguous base class (and vice versa) even if the base class is inaccessible (that is, this cast ignores the private inheritance specifier). Same applies to casting pointer to member to pointer to member of unambiguous non-virtual base;reinterpret_cast<
new-type>(
expression)
;)
and int*(expression)
are not valid), followed by a comma-separated list of expressions in parentheses.
- If there is exactly one expression in parentheses, this cast expression is exactly equivalent to the corresponding C-style cast expression.
- If there are more than one expression or braced-init-list(since C++11) in parentheses, new-type must be a class with a suitably declared constructor. This expression is a prvalue of type new-type designating a temporary(until C++17)whose result object is(since C++17) direct-initialized with expression-list.
- If there's no expression in parentheses: if new-type names a non-array complete object type, this expression is a prvalue of type new-type, designating a temporary(until C++17)whose result object is (possibly with added cv-qualifiers)(since C++17) of that type. If new-type is an object type, the object is value-initialized. If new-type is (possibly cv-qualified) void, the expression is a void prvalue without a result object(since C++17).
auto
specifier is replaced with the deduced type of the invented variable x
declared with auto x(expression);
(which is never interpreted as a function declaration) or auto x{expression};
respectively. The result is always a prvalue of an object type.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);
|
(since C++11) |
- a prvalue otherwise.
Ambiguity Resolution
In the case of an ambiguity between an expression statement with a function-style cast expression as its leftmost subexpression and a declaration statement, the ambiguity is resolved by treating it as a declaration. This disambiguation is purely syntactic: it does not consider the meaning of names occurring in the statement other than whether they are type names:
struct M {}; struct L { L(M&); }; M n; void f() { M(m); // declaration, equivalent to M m; L(n); // ill-formed declaration L(l)(m); // still a declaration }
The ambiguity above can also occur in the context of a declaration. In that context, the choice is between an object declaration with a function-style cast as the initializer and a declaration involving a function declarator with a redundant set of parentheses around a parameter name. The resolution is also to consider any construct, such as the potential parameter declaration, that could possibly be a declaration to be a declaration:
struct S { S(int); }; void foo(double a) { S w(int(a)); // function declaration: has a parameter `a` of type int S x(int()); // function declaration: has an unnamed parameter of type int(*)() // that is adjusted from int() // Ways to avoid ambiguity: S y((int(a))); // object declaration: extra pair of parentheses S y((int)a); // object declaration: C-style cast S z = int(a); // object declaration: no ambiguity for this syntax }
An ambiguity can arise from the similarity between a function-style cast and a type-id. The resolution is that any construct that could possibly be a type-id in its syntactic context shall be considered a type-id:
// `int()` and `int(unsigned(a))` can both be parsed as type-id: // `int()` represents a function returning int // and taking no argument // `int(unsigned(a))` represents a function returning int // and taking an argument of type unsigned void foo(signed char a) { sizeof(int()); // type-id (ill-formed) sizeof(int(a)); // expression sizeof(int(unsigned(a))); // type-id (ill-formed) (int()) + 1; // type-id (ill-formed) (int(a)) + 1; // expression (int(unsigned(a))) + 1; // type-id (ill-formed) }
Example
#include <cassert> #include <iostream> double f = 3.14; unsigned int n1 = (unsigned int)f; // C-style cast unsigned int n2 = unsigned(f); // function-style cast class C1; class C2; C2* foo(C1* p) { return (C2*)p; // casts incomplete type to incomplete type } 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); // prints x:2 y:2, because param y here is an alias of p int q{1}; inc_print(q, auto{q}); // prints x:2 y:1, auto{q} (C++23) casts to prvalue, // so the param y is a copy of q (not an alias of q) } // In this example, C-style cast is interpreted as static_cast // even though it would work as reinterpret_cast struct A {}; struct I1 : A {}; struct I2 : A {}; struct D : I1, I2 {}; int main() { D* d = nullptr; // A* a = (A*)d; // compile-time error A* a = reinterpret_cast<A*>(d); // this compiles assert(a == nullptr); cpp23_decay_copy_demo(); }
Output:
x:2 y:2 x:2 y:1
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 2620 | C++98 | the resolution of this ambiguity might be misinterpreted | improved the wording |
References
- C++23 standard (ISO/IEC 14882:2024):
- 7.6.1.4 Explicit type conversion (functional notation) [expr.type.conv]
- 7.6.3 Explicit type conversion (cast notation) [expr.cast]
- C++20 standard (ISO/IEC 14882:2020):
- 7.6.1.4 Explicit type conversion (functional notation) [expr.type.conv]
- 7.6.3 Explicit type conversion (cast notation) [expr.cast]
- C++17 standard (ISO/IEC 14882:2017):
- 8.2.3 Explicit type conversion (functional notation) [expr.type.conv]
- 8.4 Explicit type conversion (cast notation) [expr.cast]
- C++14 standard (ISO/IEC 14882:2014):
- 5.2.3 Explicit type conversion (functional notation) [expr.type.conv]
- 5.4 Explicit type conversion (cast notation) [expr.cast]
- C++11 standard (ISO/IEC 14882:2011):
- 5.2.3 Explicit type conversion (functional notation) [expr.type.conv]
- 5.4 Explicit type conversion (cast notation) [expr.cast]
- C++03 standard (ISO/IEC 14882:2003):
- 5.2.3 Explicit type conversion (functional notation) [expr.type.conv]
- 5.4 Explicit type conversion (cast notation) [expr.cast]
- C++98 standard (ISO/IEC 14882:1998):
- 5.2.3 Explicit type conversion (functional notation) [expr.type.conv]
- 5.4 Explicit type conversion (cast notation) [expr.cast]
See also
const_cast conversion
|
adds or removes const |
static_cast conversion
|
performs basic conversions |
dynamic_cast conversion
|
performs checked polymorphic conversions |
reinterpret_cast conversion
|
performs general low-level conversions |
standard conversions | implicit conversions from one type to another |
C documentation for cast operator
|