C++ Core Guidlines を自分の言葉で学ぶ -Philosophy:哲学 -
P.1 コードでアイデアを表現する
コンパイラはコメントを読まないし、プログラマもコメントを読むとは限らない。
class Date{
public:
Month month() const; //OK:月を返すこと、状態を変更しないことが明確
int month(); //NG:整数が表すのは何月か、0や-1が使われるのか...推測になる。状態を変更する可能性が捨てられない。
};
void change_speed(Speed s); //OK
void change_speed(double s); //NG:sの単位が不明瞭
change_speed(2.3_ms); //OK:単位が(m/s)だとわかる
change_speed(2.3); //NG:sの単位が不明瞭
※_msはユーザ定義リテラル
cppreference → https://cpprefjp.github.io/lang/cpp11/user_defined_literals.html
確認 →
P.2 ISO Standard C++で書く
環境依存の拡張機能や、環境依存の未定義動作は使わないようにする
P.3 Express Intent (意図を示す)
型や言語機能を使い、誤読の可能性を減らす
//NG: indexがループ処理以降も生き残り、何かに使われる可能性がある(実際はともかく)
int index = 0;
while(index < v.size()){
//...
}
//OK: ループ処理であることが明確
for(const auto& x: v){ ... }
draw_line(int,int,int,int); //NG:(x1,y1,x2,y2)か(x,y,w,h)か不明瞭
draw_line(Point, Point); //OK
P.4 静的な型安全が望ましい
理想的には完全に静的にでありたいが、実際にはいくつかの領域で不可能である。いくらかは代替案によって、問題を軽減することができる。
||代替案|
|共用体|std::variant|
|cast|テンプレート(※1)|
|配列のdecay|gsl::spanを使う(※2)|
|範囲エラー|gsl::spanを使う(※2)|
|nallow cast|gsl::narrow_castを使う(※3)|
※1 値をcastして処理するのではなく、テンプレートによって型をそのまま使って処理できる設計を使おう...という意味だと思う
※2 gslはCppCoreGuidelinesが提供するライブラリ。gsl::spanは配列やvectorなどの動的/静的配列の「区間」を安全に使うための型
※3 情報の損失を伴う数値のcast。gsl::narrow/gsl::narrow_castはデータの損失を厳密に扱うcast。narrowは損失がある場合にexceptionを投げるcast。narrow_castはデータの損失があってもそれを許容することを明示するcast。
P.5 実行時よりコンパイル時にエラーチェックしたい
//NG: 実行時にビット長を検査している
int bitCount = 0;
for(Int i=1; i!=0; i<<=1){ bitCount++;}
if(bitCount < 32){cerr << "too small";}
//OK: コンパイル時にビット長を検査している
static_assert(sizeof(Int) >= 4);
P.6 コンパイル時にエラーチェックできないなら実行時にエラーチェックする
特筆なし
P.7 実行時エラーチェックは、早期のキャッチを目指す
nullptrアクセスや不正な参照エラーになるよりも早く検出できるなら、そのほうが良い。謎のクラッシュを回避しよう。
//NG: pの範囲外にアクセスしてエラーになる。(または運良く/運悪く、エラーにならない)
void increment(int* p, int n){
for(int i=0;i<n;i++) ++p[i];
}
//OK: gsl::spanの生成時点で不正な範囲をさせないようになっている
void increment(span<int> p){
for(int& x: p) ++x;
}
ただし、過剰なエラーチェックは避けたい。
同じチェックが複数回行われるフローや、計算量が本来の処理を超えるようなチェックを避けたい。
P.8 リソースをリークさせない
- fopenで開いたファイルをfcloseせずに終わるとそれはリソースリーク。
- newしたオブジェクトをそのまま提供しない、
RAIIの考え方を徹底する
P.9 時間、メモリを無駄にしない
理由のない、ただの無駄は排除すべし。
//NG
void lower(char* str){
for(int i=0;i<strlen(str);i+){s[i] = tolower(s[i]0);}
}
P10. 可変より不変。
変数よりも定数のほうが推論が容易。
(定数と不変性の項目を参照のこと。)
P11. 乱雑な構築処理を書かない。カプセル化する。
オブジェクトの生成において、今やmallocなどの低レベルなAPIを使う必要はない。
標準ライブラリやgslなど、低レベルなAPIを隠蔽して使用できる道具がが多数用意されている。それらはユーザよりも専門知識のある人々によって設計・実装されている。
P12. 支援ツールを適切に使用する
- 静的解析ツール
- Concurrency Tool (?)
- テスト支援ツール
使おう
P13. ライブラリを適宜使用する
- 標準ライブラリ
- gslライブラリ
P:哲学は、以上
Discussion