初めに
Google C++ Style Guide
ライセンスCC-By 3.0 License
ttsukiさんの日本語訳
ライセンスCC-By 3.0 License
前回の記事
Scoping
コードは名前空間の中に置く。
名前空間にはユニークな名前を使用する。
using namespaceは使用しない。
インライン名前空間は使用しない。
また、複数行に渡る名前空間の末尾にはコメントをつける。
// .h
namespace foo{
public:
void Hoge();
} // namespace foo
// .cc
namespace foo {
void foo:Hoge(){
}
} // namespace foo
名前空間namespaceではインデントしない。
Internal Linkage
定義が.cc内にあり、ファイル外空参照される必要がない場合には、無名の名前空間におくか、static宣言することで、内部リンケージとなるようにする。
これらの条件に合うコードは内部リンケージにすることが推奨されている。
.hでは使用禁止。
Nonmember, Static Member, and Global Functions
非メンバ関数は何らかの名前空間におく。
完全なグローバル関数は使用しない。
静的メンバ関数はそのクラスと強い関連がある場合のみ使用する。
Local Variables
関数内でのローカル変数宣言はできる限りスコープを狭くする。
また、変数は宣言と初期化を同時に行う。
変数は局所的なスコープ内で、初めて使用する箇所の近くで宣言する。
ただし変数がオブジェクト型であるときは、スコープに入るたびコンストラクタが呼ばれることに注意する。
- int i;
- i = f();
+ int i = f();
- std::vector<int> v;
- v.push_back(1);
- v.push_back(2);
+ std::vector<int> v = {1, 2};
thread_local Variables
thread_local変数が関数スコープ外で宣言されるときにはコンパイル時定数で初期化される必要がある。
これはconstinit/constexpr属性を使用する。
また、スレッドごとにデータを定義する場合にはthread_localを使用する。
Classes
Doing Work in Constructors
コンストラクタで仮想関数を呼ばない。
また、エラーを起こす可能性のある初期化処理をおこなうべきではない。(強制終了させる。)
mplicit Conversions
暗黙的型変換を定義しない。
型変換演算子やコンストラクタにはexplicitキーワードを使用する。
Copyable and Movable Types
クラスを定義する場合には、その型のオブジェクトがコピー可能か、ムーブのみ可能か、いずれも不可能なのかを明確化する必要がある。
継承における基底クラスは抽象クラスとするほうが望ましい。
そのため、コンストラクタ/デストラクタをprotectedにする、もしくは1つ以上の純粋仮想メンバ関数を宣言する。
また、基底クラスより派生したクラスからさらなる派生クラスを作成することは避けたほうが良い。
Structs vs. Classes
structはデータを格納するための受動的なオブジェクトにのみ使用する。
その他の場合classを使用する。
structは関連する定数を含んでも良く、すべてのフィールドはpublicである必要がある。
状態を何も持たない型(型トレイト、関数オブジェクト)についてはSTLとの一貫性のためにstructも使用できる。
Structs vs. Pairs and Tuples
要素に意味ある命名が可能ならば、structを使用するべきである。
これはコードの可読性を高めるためである。
汎用的なコードまたは、既存コードとの互換性のためにPairやTuplesも使用できる。
Inheritance
継承よりもcompositionを用いる場合も多い。
継承を用いるときには必ずpublicにする。
privateな継承を行う場合には基底クラスのインスタンスをメンバとして持つようにする。
基底クラスとして使用しないクラスにはfinalを指定できる。
仮想関数などのオーバーライドにはoverride指定子を記述し、オーバーライドを明示する。(この目的のためにvirtualは使用しない)
多重継承は非推奨。
Operator Overloading
ユーザー定義リテラルを使用しない。
演算子オーバーロードはその意味が明らかであり、動作を予測可能で、かつ組み込み演算子との一貫性が保てるときに限る定義する。
また、型Tが==で比較できるなら、それを定義した上で、等値とみなされる条件をドキュメント化する。
演算子&&、||、,、&とoperator""はオーバーロードしない。
Access Control
クラスのデータメンバは定数を覗いてprivateにする。
Declaration Order
public:、protected:、private:の順で宣言する。
中身のないセクションは省略する。
各セクションでは以下の順に並べる。
- 型、型エイリアス
- 非Staticメンバ変数
- 静的変数
- ファクトリ関数
- コンストラクタと代入演算子
- デストラクタ
- その他すべての関数
- 上記以外のデータメンバ
Functions
Inputs and Outputs
関数の出力は引数よりも戻り値を用いるほうが望ましい。
戻り地は可能な限り値渡しにし、それ以外の場合は参照を返す。
null参照になる場合以外はポインタを返すことは避ける。
入力必須の引数はね私かconst参照渡しで宣言する。
必須でないものはstd::optionalで値渡しとするかconstポインタを使用する。
引数の順序は、入力、出力の順とする。
Write Short Functions
関数は短く。
40行程度を超える場合には関数を分割することを検討する。
Function Overloading
ユーザーが具体的にどの関数が呼び出されるか、正確に知る必要はない (動作がわかれば良い)。
Default Arguments
仮想関数でなく、引数の値が同じ値となることを保証している場合に限り、引数にデフォルト値を与えても良い。
ただし、これによって得られる可読性よりも、意図に反する動作をする場合にはこの限りでない。
Trailing Return Type Syntax
戻り値の型の後置は、従来の方法では可読性が非常に劣る場合に限り用いる。
多くの場合には戻り値の型を関数名の前に記述する従来の方法を用いる。
例:ラムダ式やテンプレートパラメータなど
- template <typename T, typename U>
- auto add(T t, U u) -> decltype(t + u);
+ template <typename T, typename U>
+ decltype(declval<T&>() + declval<U&>()) add(T t, U u);