はじめに
C++について詳しく勉強しようと思い、プログラミング言語C++[第4版]を読んでいます。
気になった部分をまとめたいと思います。(気になったところから読んでいるので読む章の順番はバラバラです)
今回は第6章「型と宣言」の「初期化」についてです。
第6章 型と宣言
初期化
X
を型とした時、オブジェクトの初期化子には以下の4種類があります。
X a1 {v};
X a2 = {v};
X a3 = v;
X a4(v);
これらのうち、あらゆる局面で利用できるのは先頭の{}形式です。この形式は、{}を利用した並びによる初期化と呼ばれ、縮小変換を許しません。縮小変換とは、異なった型同士での代入や式の中で組み合わせる時に、情報を失わないように値が変換されることです。
以下の例のように{}を利用した並びによる初期化を用いることで縮小変換を防ぐことができます。
void f(double val, int val2) {
// 縮小変換を許す
int x2 = val; // もしval==7.9ならばx2は7
char c2 = val2; // もしval2==1025ならばc2は1
// 縮小変換を許さない ({}を利用した並びによる初期化)
int x3 {val}; // エラー : 切り捨ての可能性
char c3 {val2}; // エラー : 縮小の可能性
}
デフォルト値による初期化を行うには、{}による初期化を使い、以下のように適切に表現されたゼロで初期化されます。
int x4 {}; // x4は0
double d4 {}; // d4は0.0
char* p {}; // pはnullptr
vector<int> v4 {}; // v4は空のvector
string s4 {}; // s4は""
しかし、autoを利用して初期化子に基づいて肩を取り出す場合は、{}を利用した並びによる初期化では、以下のようにinitializer_listであると型を推論される落とし穴があるので、=を用いたほうが良いです。
auto z1 {99}; // z1はinitializer_list<int>
auto z2 = 99; // z2はint
それでもautoで型推論するような特別な理由がない限りは、{}による初期化を優先したほうが良いみたいです。
初期化子の省略
多くの型が初期化子を必須としておらず、省略することができます。
静的オブジェクトの初期化子を省略すると、それぞれに適した型の{}で初期化されます。
int a; // "int a{};"のことなので0
double d; // "double d{};"のことなので0.0
動的オブジェクト(ヒープオブジェクト)の初期化子を省略すると、デフォルトコンストラクタを持つユーザ定義型でない限り、デフォルトでは初期化されません。
void f() {
string s; // stringのデフォルトコンストラクタによってs==""
int x; // xは的確に定義された値を持たない
int* p {new int}; // *pは的確に定義された値を持たない
}
初期化子並び
初期化子として2個以上の値を与えた場合には、以下のように{と}で囲んだ初期化子並びを用います。
int a[] = { 1, 2 }; // 配列初期化子
vector<double> v = { 0.0, 1.1, 2.2, 3.3 }; // 並びコンストラクタを利用
この例で=は冗長ですが、メンバとなる変数を初期化する値であることを強調するために、あえて記述するというスタイルもあるので、どちらでも良いです。
おわりに
初期化するときは基本的に{}を利用しましょう。