コーディングスタイルは最終的に主観的なものであり、パフォーマンススタイルが大幅に向上することはほとんどありません。しかし、ここで私はあなたが均一な初期化の寛大な使用から得るものだと言うでしょう:
冗長な型名を最小限に抑える
以下を考慮してください。
vec3 GetValue()
{
return vec3(x, y, z);
}
なぜvec3
2回入力する必要があるのですか?それにはポイントがありますか?コンパイラーは、関数が何を返すかをよく知っています。「これらの値で返すもののコンストラクターを呼び出して返す」と言うことができないのはなぜですか?均一な初期化により、次のことができます。
vec3 GetValue()
{
return {x, y, z};
}
すべてが機能します。
さらに良いのは、関数の引数です。このことを考慮:
void DoSomething(const std::string &str);
DoSomething("A string.");
暗黙的にstd::string
自分自身を構築する方法を知っているため、タイプ名を入力することなく機能しconst char*
ます。それは素晴らしいことです。しかし、RapidXMLの場合、その文字列の由来はどうでしょうか。またはLua文字列。つまり、文字列の長さを実際に知っているとしましょう。std::string
取るコンストラクタは、const char*
私は合格した場合、文字列の長さを取る必要がありますconst char*
。
ただし、明示的に長さがかかるオーバーロードがあります。しかし、それを使用するには、これを行う必要がありますDoSomething(std::string(strValue, strLen))
。なぜそこに余分な型名があるのですか?コンパイラは、型が何であるかを知っています。のようにauto
、余分な型名を避けることができます:
DoSomething({strValue, strLen});
それはただ動作します。型名も大騒ぎもなし。コンパイラがその仕事をし、コードが短くなり、誰もが満足しています。
確かに、最初のバージョン(DoSomething(std::string(strValue, strLen))
)の方が読みやすいという議論があります。つまり、何が起こっているのか、誰が何をしているのかは明らかです。それはある程度真実です。均一な初期化ベースのコードを理解するには、関数プロトタイプを調べる必要があります。これは、値が変更されているかどうかを呼び出しサイトで確認できるように、非定数参照でパラメーターを渡すべきではないと言う人がいるのと同じ理由です。
しかし、同じことが言えauto
ます。何から得られるかを知るにauto v = GetSomething();
は、の定義を調べる必要がありGetSomething
ます。しかし、auto
一度アクセスしてしまえば、それは無謀に近い放棄で使用されることを止めていません。個人的には、慣れれば大丈夫だと思います。特に良いIDEで。
最も厄介な解析を取得しない
ここにいくつかのコードがあります。
class Bar;
void Func()
{
int foo(Bar());
}
ポップクイズ:何foo
ですか?「変数」と答えた場合、あなたは間違っています。実際には、aを返す関数をパラメーターとして受け取る関数のプロトタイプでBar
あり、foo
関数の戻り値はintです。
これは、C ++の「Most Vexing Parse」と呼ばれ、人間にはまったく意味がないためです。しかし、C ++のルールでは悲しいことにこれが必要です。関数プロトタイプとして解釈できる可能性がある場合は、そうなります。問題はBar()
; それは2つのことのいずれかです。という名前のタイプである可能性がありBar
ます。つまり、一時的なものを作成しています。または、パラメータを取らずにを返す関数でもかまいませんBar
。
均一な初期化は、関数プロトタイプとして解釈できません。
class Bar;
void Func()
{
int foo{Bar{}};
}
Bar{}
常に一時を作成します。int foo{...}
常に変数を作成します。
使用したいTypename()
が、C ++の解析ルールのために使用できない場合が多くあります。でTypename{}
、あいまいさはありません。
しない理由
あなたが放棄する唯一の真の力は、狭めることです。均一な初期化では、小さな値を大きな値で初期化することはできません。
int val{5.2};
それはコンパイルされません。旧式の初期化を使用してこれを行うことができますが、均一な初期化はできません。
これは、初期化リストを実際に機能させるために部分的に行われました。そうでなければ、初期化子リストのタイプに関して多くのあいまいなケースがあります。
もちろん、そのようなコードはコンパイルしないに値すると主張する人もいるかもしれません。私は個人的に同意します。絞り込みは非常に危険であり、不快な動作につながる可能性があります。コンパイラーの段階でこれらの問題を早期に発見することをお勧めします。少なくとも、狭めることは、誰かがコードについてあまり熱心に考えていないことを示唆しています。
警告レベルが高い場合、コンパイラは一般にこの種のことについて警告します。本当に、これは警告を強制エラーにすることです。とにかくそうするべきだと言う人もいるかもしれません;)
しない理由がもう1つあります。
std::vector<int> v{100};
これは何をしますか?vector<int>
100個のデフォルトで構築されたアイテムを作成できます。または、vector<int>
値が1 のwithアイテムを作成できます100
。両方とも理論的には可能です。
実際には、後者を行います。
どうして?初期化リストは、統一初期化と同じ構文を使用します。そのため、あいまいな場合の対処方法を説明するためのいくつかのルールが必要です。ルールは非常に単純です。コンパイラがブレースで初期化されたリストを持つ初期化子リストコンストラクターを使用できる場合、それはになります。以来、vector<int>
かかる初期化リストコンストラクタ有しinitializer_list<int>
、及び{100}有効である可能性をinitializer_list<int>
、したがって、でなければなりません。
サイズ設定コンストラクターを取得するには、の()
代わりに使用する必要があります{}
。
これがvector
整数に変換できないものである場合、これは起こらないことに注意してください。initializer_listはそのvector
タイプの初期化子リストコンストラクターに適合しないため、コンパイラは他のコンストラクターから自由に選択できます。