明示的な型変換
From cppreference.com
明示的変換と暗黙的変換の組み合わせを使用して型間を変換します。
目次 |
[編集] 構文
( 型指定子 ) 単項式 |
(1) | ||||||||
simple-type-specifier ( expression-list (optional) )simple-type-specifier ( initializer-list (optional) ) |
(2) | (C++11まで) (C++11以降) | |||||||
simple-type-specifier { initializer-list (optional) } |
(3) | (C++11以降) | |||||||
simple-type-specifier { designated-initializer-list } |
(4) | (C++20以降) | |||||||
typename identifier ( initializer-list (optional) ) |
(5) | (C++11以降) | |||||||
typename identifier { initializer-list (optional) } |
(6) | (C++11以降) | |||||||
typename identifier { designated-initializer-list } |
(7) | (C++20以降) | |||||||
任意の数の値をターゲット型の値に明示的に変換します。
1) 明示的な型変換 (キャスト記法)。「Cスタイルキャスト」とも呼ばれます。
2-7) 明示的な型変換 (関数記法)。「関数スタイルキャスト」とも呼ばれます。
| type-id | - | 型指定子 |
| 単項式 | - | 単項式 (トップレベルの演算子の優先順位がCスタイルキャストの優先順位よりも高くないもの) |
| simple-type-specifier | - | 単純型指定子 |
| expression-list (式リスト) | - | カンマ区切りの式のリスト (括弧で囲まれていないカンマ式を除く) |
| initializer-list (初期化子リスト) | - | カンマ区切りの初期化句のリスト |
| designated-initializer-list | - | カンマ区切りの指定初期化句のリスト |
| identifier | - | (修飾子付きの可能性もある) 識別子 (テンプレート識別子を含む) |
[編集] 解説
1) Cスタイルキャストが検出されると、コンパイラはそれを以下のキャスト式として、この順序で解釈しようとします。
a)
const_cast<型指定子 >(単項式 );b)
static_cast<型指定子 >(単項式 ), 拡張付き: 派生クラスへのポインタまたは参照は、基底クラスがアクセス不能な場合でも、曖昧でない基底クラスへのポインタまたは参照にキャストできる (およびその逆も可能) (つまり、このキャストはprivate継承指定子を無視します)。メンバへのポインタを曖昧でない非仮想基底のメンバへのポインタにキャストする場合も同様です;c) static_cast (拡張付き) に続いて const_cast;
d)
reinterpret_cast<型指定子 >(単項式 );e) reinterpret_cast に続いて const_cast。
各キャスト演算子の要件を満たす最初の選択肢が、それが不適格な形式であっても (例を参照)、選択されます。static_cast に続いて const_cast が使用され、その変換が複数通りに解釈できる場合、その変換は不適格な形式です。
さらに、Cスタイルキャストは、不完全なクラス型へのポインタとの間で、またそれらの間でキャストできます。型指定子と単項式の型が両方とも不完全なクラス型へのポインタである場合、static_cast または reinterpret_cast のどちらが選択されるかは未指定です。
2-7) 関数スタイルキャストは型 (simple-type-specifier または identifier (C++11以降)) と初期化子 (残りの部分) を指定し、指定された型 および初期化子(C++17以降)から決定されるターゲット型
Tの値を構築します。|
|
(C++17まで) | ||
|
|
(C++17以降) |
変換結果は次のように決定されます。
- 関数スタイルキャストが構文(2)であり、括弧内に式がちょうど1つある場合、このキャストは対応するCスタイルキャストと同等です。
- そうでなければ、
Tが (cv修飾された可能性のある) void型である場合、結果はrvalue(C++11まで)prvalue(C++11以降)のvoid型であり、初期化は行われません。
|
(C++11まで) |
|
(C++11以降) |
- そうでなければ、
Tが参照型である場合、関数スタイルキャストは、指定された初期化子から型Tの架空の変数tを直接初期化するのと同じ効果を持ち、結果は初期化されたtです。
|
(C++11まで) |
|
(C++11以降) |
- そうでなければ、結果はrvalue(C++11まで)prvalue(C++11以降)の
T型であり、一時オブジェクトを指定する(C++17まで)結果オブジェクトが(C++17以降)指定された初期化子で直接初期化されます。
[編集] 曖昧性の解決
[編集] 曖昧な宣言文
左端の副式として関数スタイルキャスト式を持つ式文と宣言文の間の曖昧さの場合、その曖昧さは宣言として扱うことで解決されます。この曖昧さの解消は純粋に構文的なものであり、文中に現れる名前の意味は型名であるかどうか以外は考慮されません。
struct M {}; struct L { L(M&); }; M n; void f() { M(m); // declaration, equivalent to M m; L(n); // ill-formed declaration, equivalent to L n; L(l)(m); // still a declaration, equivalent to L l((m)); }
|
ただし、曖昧な宣言文の最も外側の宣言子に後置戻り値の型がある場合、その文は後置戻り値の型がautoで始まる場合にのみ宣言文として扱われます。 struct M; struct S { S* operator()(); int N; int M; void mem(S s) { auto(s)()->M; // expression (S::M hides ::M), invalid before C++23 } }; void f(S s) { { auto(s)()->N; // expression, invalid before C++23 auto(s)()->M; // function declaration, equivalent to M s(); } { S(s)()->N; // expression S(s)()->M; // expression } } |
(C++11以降) |
[編集] 曖昧な関数パラメータ
上記の曖昧さは、宣言のコンテキストでも発生する可能性があります。そのコンテキストでは、初期化子として関数スタイルキャストを持つオブジェクト宣言と、パラメータ名の周りに冗長な括弧のセットを持つ関数宣言子を含む宣言の間で選択が行われます。解決策は、潜在的なパラメータ宣言のような、宣言である可能性がある任意の構造体を宣言として考慮することです。
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 }
|
ただし、曖昧なパラメータ宣言の最も外側の宣言子に後置戻り値の型がある場合、その曖昧さはそれがautoで始まる場合にのみ宣言として扱うことで解決されます。 typedef struct BB { int C[2]; } *B, C; void foo() { S a(B()->C); // object declaration: B()->C cannot declare a parameter S b(auto()->C); // function declaration: has an unnamed parameter of type C(*)() // that is adjusted from C() } |
(C++11以降) |
[編集] 曖昧な型指定子
関数スタイルキャストと型指定子の類似性から曖昧さが発生する可能性があります。その解決策は、構文コンテキストで型指定子である可能性がある任意の構造体を型指定子と見なすことです。
// `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) }
|
ただし、曖昧な型指定子の最も外側の抽象宣言子に後置戻り値の型がある場合、その曖昧さはそれがautoで始まる場合にのみ型指定子として扱うことで解決されます。 typedef struct BB { int C[2]; } *B, C; void foo() { sizeof(B()->C[1]); // OK, sizeof(expression) sizeof(auto()->C[1]); // error: sizeof of a function returning an array } |
(C++11以降) |
[編集] 注釈
| 機能テストマクロ | 値 | 規格 | 機能 |
|---|---|---|---|
__cpp_auto_cast |
202110L |
(C++23) | auto(x) と auto{x} |
[編集] 例
このコードを実行
#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(); }
出力
x:2 y:2 x:2 y:1
[編集] 欠陥レポート
以下の動作変更を伴う欠陥報告が、以前に公開されたC++標準に遡って適用されました。
| DR | 適用対象 | 公開された動作 | 正しい動作 |
|---|---|---|---|
| CWG 1223 (P2915R0) |
C++11 | 後置戻り値の型の追加により、さらに多くの曖昧さが導入された | それらを解決する |
| CWG 1893 | C++11 | 関数スタイルキャストはパック展開を考慮しなかった | それらを考慮する |
| CWG 2351 | C++11 | void{} は不正な形式だった | 適切な形式になった |
| CWG 2620 | C++98 | 曖昧な関数の解決 パラメータが誤解される可能性がある |
記述を改善しました |
| CWG 2828 | C++98 | 複数の解釈がある場合、Cスタイルキャストは不正な形式だった。 static_cast に続く const_cast が存在する場合、 これらの変換が実際に使用されるかどうかに関わらず |
のみ考慮する 変換を適用できる 使用される可能性がある |
| CWG 2894 | C++98 | 関数スタイルキャストは参照右辺値を作成できた | 参照左辺値のみ作成できる |
[編集] 参照
- C++23標準 (ISO/IEC 14882:2024)
- 7.6.1.4 明示的な型変換 (関数記法) [expr.type.conv]
- 7.6.3 明示的な型変換 (キャスト記法) [expr.cast]
- C++20 standard (ISO/IEC 14882:2020)
- 7.6.1.4 明示的な型変換 (関数記法) [expr.type.conv]
- 7.6.3 明示的な型変換 (キャスト記法) [expr.cast]
- C++17 standard (ISO/IEC 14882:2017)
- 8.2.3 明示的な型変換 (関数記法) [expr.type.conv]
- 8.4 明示的な型変換 (キャスト記法) [expr.cast]
- C++14 standard (ISO/IEC 14882:2014)
- 5.2.3 明示的な型変換 (関数記法) [expr.type.conv]
- 5.4 明示的な型変換 (キャスト記法) [expr.cast]
- C++11 standard (ISO/IEC 14882:2011)
- 5.2.3 明示的な型変換 (関数記法) [expr.type.conv]
- 5.4 明示的な型変換 (キャスト記法) [expr.cast]
- C++03 標準 (ISO/IEC 14882:2003)
- 5.2.3 明示的な型変換 (関数記法) [expr.type.conv]
- 5.4 明示的な型変換 (キャスト記法) [expr.cast]
- C++98 標準 (ISO/IEC 14882:1998)
- 5.2.3 明示的な型変換 (関数記法) [expr.type.conv]
- 5.4 明示的な型変換 (キャスト記法) [expr.cast]
[編集] 関連項目
const_cast 変換
|
const を追加または削除する |
static_cast 変換
|
基本的な変換を実行する |
dynamic_cast 変換
|
チェックされた多態的変換を実行する |
reinterpret_cast 変換
|
一般的な低レベル変換を実行する |
| 標準変換 | ある型から別の型への暗黙的な変換 |
| C 言語ドキュメント (キャスト演算子)
| |