Google C++ スタイルガイド 日本語全訳
訳者前書き
- 本ドキュメントはhttps://github.com/google/styleguide/ にて公開されているGoogle C++ Style Guideを日本語に訳したものです。
- オリジナルの最新版は https://google.github.io/styleguide/cppguide.htmlで読むことができます。
- commit 8c4da23 (Sep 6, 2024 (JST)) 時点のものを底本としています。
- オリジナルと同様CC-By 3.0 Licenseで頒布します。
- 誤訳を含んでいる恐れがあります。訳文にマウスを重ねると原文が表示されますので、そちらもあわせて参照してください。
- 訳者はGoogleと無関係です。
- 誤訳の指摘・改善案は GitHub の Issues か Pull Requests からお願いします。
- ☆をつけてもらえると励みになります;) Star:
背景
C++ is one of the main development languages used by many of Google's open-source projects. C++は、多くのGoogleのオープンソースプロジェクトで採用されている主要言語のうちのひとつです。 As every C++ programmer knows, the language has many powerful features, but this power brings with it complexity, which in turn can make code more bug-prone and harder to read and maintain. C++プログラマーなら誰でも知っているように、この言語にはたくさんのパワフルな機能が備わっていますが、それと同時に非常に複雑でもあります。この複雑さこそが、ときにバグを埋め込む要因となったり、あるいは、コードを読みにくくメンテナンスしにくくしてしまう要因となったりしています。
The goal of this guide is to manage this complexity by describing in detail the dos and don'ts of writing C++ code. このガイドでは、C++のコードを書くときにすべきこと・すべきでないことを詳しく説明し、C++のもつ様々な複雑さに対処できるようにすることを目標としています。 These rules exist to keep the code base manageable while still allowing coders to use C++ language features productively. これらのルールに従えば、C++の生産的な言語機能を活かしつつ、コードベースを管理可能な状態に保つことができるでしょう。
Style, also known as readability, is what we call the conventions that govern our C++ code. 「スタイル」という語は、一般には可読性(readability/リーダビリティ)としても知られていますが、このガイドにおけるスタイルは、私たちのC++コードが従うべきコーディング規約全般のことを指しています。 The term Style is a bit of a misnomer, since these conventions cover far more than just source file formatting. これらの規約では、単なるソースコードのフォーマットにとどまらず、もっと広い範囲をカバーしていますので、その意味で、スタイルという用語を充てるのは少し語弊があるかもしれません。
Most open-source projects developed by Google conform to the requirements in this guide. ほとんどのGoogleのオープンソースプロジェクトが、このガイドの要件に沿って開発されています。
Note that this guide is not a C++ tutorial: we assume that the reader is familiar with the language. なお、このガイドはC++のチュートリアルではありません。むしろ、このガイドはC++に精通した読者に向けて書かれていますので、その点には注意してください。
このスタイルガイドのゴール
Why do we have this document? 何のために、この文書があるのでしょうか?
There are a few core goals that we believe this guide should serve. まず、このガイドが果たそうとする中心的な目標、すなわちゴールがいくつかあります。 These are the fundamental whys that underlie all of the individual rules. それらは、このガイドで説明されるすべての個別のルールの根底にある、基礎的な「なぜそのようにするのか」の集合です。 By bringing these ideas to the fore, we hope to ground discussions and make it clearer to our broader community why the rules are in place and why particular decisions have been made. 私たちは、これらの考え方を前面に出すことで、より広いコミュニティにおいて、議論を根付かせ、なぜそのルールがあるのか、なぜそのように決められているのか、より明確に理解されるようにしていきたいと考えています。 If you understand what goals each rule is serving, it should be clearer to everyone when a rule may be waived (some can be), and what sort of argument or alternative would be necessary to change a rule in the guide. このガイドで説明される各ルールの存在が、それぞれ何を達成しようとしているのか、その目的を正しく理解できれば、同時に、どのような場合にそのルールを放棄すべきか、あるいは、ルールを変更するために必要な議論や代替案についても、より考えやすくなることでしょう。
The goals of the style guide as we currently see them are as follows: 現時点での本スタイルガイドのゴールは以下のとおりです。
- Style rules should pull their weight それぞれのスタイルルールが十分役立つものであること
-
The benefit of a style rule
must be large enough to justify asking all of our engineers to
remember it.
各スタイルルールがもたらす利益は、所属するすべてのエンジニアが覚えるに値するくらい、十分に価値のあるものでなければいけません。
The benefit is measured relative to the codebase we would
get without the rule, so a rule against a very harmful practice may
still have a small benefit if people are unlikely to do it
anyway.
ここでいう利益は「仮にそのルールが無かったとしたら、書かれてしまったかもしれないコード」との比較で計ります。
つまり、仮に何らかの非常に有害な慣行を制限するルールを考えたとしても、そもそもそのルールが無くても問題がない (すなわち、そのような有害な書き方は通常されない) のであれば、そのルールの価値は低いという風に考えます。
This principle mostly explains the rules we don’t have, rather
than the rules we do: for example,
goto
contravenes many of the following principles, but is already vanishingly rare, so the Style Guide doesn’t discuss it. この原則は、むしろ、このスタイルガイドで取り扱わないルールについて説明していると言えます。 たとえば、このガイドではgoto
の濫用に関するルールを取り扱っていません。goto
の濫用はこのガイドの多くの原則に違反しますが、昨今では、わざわざ禁止せずとも、goto
が濫用されること自体がほとんどなくなっているため、このルールを定めることにはあまり価値がありません。 - Optimize for the reader, not the writer 書き手ではなく、読み手のためにコードを最適化すること
-
Our codebase (and most individual components submitted to it) is
expected to continue for quite some time.
私たちのコードベース(と、それらに与する多くの個々のコンポーネント)は、今後、長い時を経ていきます。
As a result, more time will
be spent reading most of our code than writing it.
結果的には、そのコードを書くのに費やした時間に比べ、はるかに多くの時間がコードを読むために割かれることでしょう。
We explicitly
choose to optimize for the experience of our average software engineer
reading, maintaining, and debugging code in our codebase rather than
ease when writing said code.
私たちは、コードを書くときに楽をすることよりも、私たちの平均的なエンジニアにとって、コードが読みやすく、メンテしやすく、デバッグしやすくなるように最適化することを明示的に選択します。
"Leave a trace for the reader" is a
particularly common sub-point of this principle: When something
surprising or unusual is happening in a snippet of code (for example,
transfer of pointer ownership), leaving textual hints for the reader
at the point of use is valuable (
std::unique_ptr
demonstrates the ownership transfer unambiguously at the call site). 「コード読者のためのヒントを残すこと。」それがこの原則の共通のサブテーマです。コードの中で何か特殊なことをするとき(たとえば、ポインタの所有権を移すとき)、コード読者のためにコードを読み解くヒントを残すことは非常に価値のあることです。(たとえば、先ほどのポインタの所有権を移す例では、std::unique_ptr
を使えば、曖昧性なくそのことを表現できるでしょう。) - Be consistent with existing code 既存のコードとの一貫性が保たれていること
-
Using one style consistently through our codebase lets us focus on
other (more important) issues.
ある唯一のスタイルをコードベース全体で一貫して用いることで、それ以外の、本来注目すべき重要な課題にフォーカスすることができます。
Consistency also allows for automation:
tools that format your code or adjust your
#include
s only work properly when your code is consistent with the expectations of the tooling. また、一貫性を保つと、ツールによる自動化も進めやすくなります。たとえば、コード整形ツールや#include
を整理するツールは、コードがツールの期待と一貫しているときにのみ、その力を発揮することでしょう。 In many cases, rules that are attributed to "Be Consistent" boil down to "Just pick one and stop worrying about it"; the potential value of allowing flexibility on these points is outweighed by the cost of having people argue over them. 多くの場合において、「一貫性を保つ」に帰するルールは、「ある1つのやり方を選んで決め、それについて悩むのをやめる」ということに要約されます。これらの点では、多様性を認めることで得られる潜在的な価値よりも、それについて議論するコストの方が上回ってしまうからです。 However, there are limits to consistency; it is a good tie breaker when there is no clear technical argument, nor a long-term direction. ただし、「一貫性」には限度もあります。一貫性は、明確な技術的論拠や長期的な方向性等が特に無いときには、良い判断材料となります。 It applies more heavily locally (per file, or for a tightly-related set of interfaces). これは、局所的(ファイル単位、もしくは密に関連するインターフェース集合)なほど、より強く適用されます。 Consistency should not generally be used as a justification to do things in an old style without considering the benefits of the new style, or the tendency of the codebase to converge on newer styles over time. 一方で、新しいスタイルを採用するメリットや、時とともに新しいスタイルに移り変わっていくコードの傾向を考慮することなく、物事を古いスタイルのまま推し進めるための言い訳としては、「一貫性」を持ち出すべきではありません。 - Be consistent with the broader C++ community when appropriate より広範囲なC++コミュニティとの一貫性を保つこと
- Consistency with the way other organizations use C++ has value for the same reasons as consistency within our code base. 自分たちのコードベース内で一貫性を保つのと同じ理由で、C++を使う他の組織の手法との一貫性を保つことにも、やはり価値があります。 If a feature in the C++ standard solves a problem, or if some idiom is widely known and accepted, that's an argument for using it. ある課題に対してC++標準機能の範囲で目的を達成できるのならば、あるいは、あるイディオムが広く知られて受け入れられているのならば、その事実はそれらを採用する理由になるでしょう。 However, sometimes standard features and idioms are flawed, or were just designed without our codebase's needs in mind. しかし、しばしば、それらの標準機能やイディオムに不備があったり、あるいは、単に私たちのコードベースのニーズを満たさない設計になっていたりすることがあります。 In those cases (as described below) it's appropriate to constrain or ban standard features. 詳しくは後述しますが、このような場合には、それらの採用や使用について制限または禁止してしまう方が適切です。 In some cases we prefer a homegrown or third-party library over a library defined in the C++ Standard, either out of perceived superiority or insufficient value to transition the codebase to the standard interface. 場合によっては、C++標準ライブラリよりも自家製あるいはサードパーティのライブラリを選ぶこともあります。単にそれらのライブラリの方が優れている場合や、あるいは、コードベースを標準的なインターフェースに移行するほどに十分なメリットがないという場合などです。
- Avoid surprising or dangerous constructs 特殊な構成・危険な構成を避けること
- C++ has features that are more surprising or dangerous than one might think at a glance. C++の中には、一見しただけでは想像できない、驚くような挙動をするものや、あるいは危険な機能も存在しています。 Some style guide restrictions are in place to prevent falling into these pitfalls. 本ガイドでは、そのような落し穴を避けるために、いくつかの機能について使用制限を課すルールを設けています。 There is a high bar for style guide waivers on such restrictions, because waiving such rules often directly risks compromising program correctness. これらのルールを撤廃してしまうと、直接的にプログラムの正当性を損なってしまうリスクに晒されるため、そのような制限に関するルールの放棄については高いハードルがあります。
- Avoid constructs that our average C++ programmer would find tricky or hard to maintain 平均的なC++プログラマーがトリッキーに感じたり、あるいはメンテナンスしにくいと感じたりする可能性のある構成を避けること
- C++ has features that may not be generally appropriate because of the complexity they introduce to the code. C++の中には、その機能によってコードが複雑化してしまうのを避けるため、通常は採用すべきでないような機能も存在します。 In widely used code, it may be more acceptable to use trickier language constructs, because any benefits of more complex implementation are multiplied widely by usage, and the cost in understanding the complexity does not need to be paid again when working with new portions of the codebase. とはいえ、広く使われるコードであれば、多少トリッキーな言語構造をしていても、比較的受け入れられやすい場合もあります。その複雑な実装が広く使われることによって価値が倍増し、また、新たなコード中で再度その実装を扱うときには、その複雑さに対する学習コストを再度支払う必要がなくなっているためです。 When in doubt, waivers to rules of this type can be sought by asking your project leads. これについて迷ったときは、この種のルールの放棄について、プロジェクトリーダーと相談してください。 This is specifically important for our codebase because code ownership and team membership changes over time: even if everyone that works with some piece of code currently understands it, such understanding is not guaranteed to hold a few years from now. このことは、私たちのコードベースでは特に重要です。なぜなら、コードの担当者やチームのメンバーは時とともに変化していくからです。たとえ今この瞬間に、チームメンバー全員が全てのコードを理解できていたとしても、数年後も同じである保証はどこにもありません。
- Be mindful of our scale 私たち自身の規模に留意すること
- With a codebase of 100+ million lines and thousands of engineers, some mistakes and simplifications for one engineer can become costly for many. 1億行以上のコードがあり、何千人ものエンジニアが働くコードベースにおいては、ある1人のミスや単純化が、多くのエンジニアの損失を招くことがあります。 For instance it's particularly important to avoid polluting the global namespace: name collisions across a codebase of hundreds of millions of lines are difficult to work with and hard to avoid if everyone puts things into the global namespace. たとえば、グローバル名前空間の汚染を避けることは非常に重要です。何億行ものコードベースにおいて、誰もがグローバル名前空間に何でもかんでも入れていたら、名前の衝突を避けるのは困難を極めるでしょう。
- Concede to optimization when necessary 必要な最適化は容認すること
- Performance optimizations can sometimes be necessary and appropriate, even when they conflict with the other principles of this document. しばしば、パフォーマンス上の最適化を必要とする場合があります。たとえ、本ガイドの何らかのルールと競合したとしても、そのパフォーマンスの最適化が必要かつ適切なのであれば、それを行ってください。
The intent of this document is to provide maximal guidance with reasonable restriction. このドキュメントの趣旨は、合理的な制限を設けて、最大限の道しるべを提供することにあります。 As always, common sense and good taste should prevail. いつもどおり、常識と良識を優先してください。 By this we specifically refer to the established conventions of the entire Google C++ community, not just your personal preferences or those of your team. これは特に、個人やチームの好みに限らず、GoogleのC++コミュニティ全体で確立された慣習を指しています。 Be skeptical about and reluctant to use clever or unusual constructs: the absence of a prohibition is not the same as a license to proceed. 巧妙(clever)な構成や特殊な構成に対して懐疑的な姿勢を持ち、それらを避けるようにしてください。禁止されていなければやってもよいというわけではありません。 Use your judgment, and if you are unsure, please don't hesitate to ask your project leads to get additional input. 自らで判断しましょう。もし確信を持てないときは、遠慮なくプロジェクトリーダーに質問して、さらなるインプットを得るようにしてください。
C++のバージョン
Currently, code should target C++20, i.e., should not use C++23 features. 現時点において、コードはC++20をターゲットとします。C++23の機能は使ってはいけません。 The C++ version targeted by this guide will advance (aggressively) over time. なお、このガイドが対象とするC++のバージョンは、時とともに(かつ積極的に)進行します。
Do not use non-standard extensions. 非標準の拡張を使ってはいけません。
Consider portability to other environments before using features from C++17 and C++20 in your project. C++17やC++20から導入された比較的新しい機能については、それらを使う前に、他の環境への移植性について考慮してください。
ヘッダーファイル
In general, every .cc
file should have an
associated .h
file.
一般的に、全ての.cc
ファイルについて、それと対応する.h
ファイルを作るようにします。
There are some common
exceptions, such as unit tests and
small .cc
files containing just a
main()
function.
ただし、ユニットテストやmain()
だけを含むような小さな.cc
ファイルは例外としてもかまいません。
Correct use of header files can make a huge difference to the readability, size and performance of your code. 正しくヘッダーファイルを扱うことは、コードの可読性やサイズ、パフォーマンスに大きな違いをもたらします。
The following rules will guide you through the various pitfalls of using header files. 以下に続くルールは、ヘッダーファイルに関する様々な落とし穴を避けるためのガイドとなるでしょう。
自己完結ヘッダー
Header files should be self-contained (compile on their own) and
end in .h
.
ヘッダーファイルは、自己完結、すなわち、それ単体でコンパイルできるようにします。また、ファイル名は.h
で終わります。
Non-header files that are meant for inclusion
should end in .inc
and be used sparingly.
ヘッダーファイル以外で#include
されることを意図したファイルには.inc
で終わる名前を付けます。ただし、なるべく使用を控えるようにしましょう。
All header files should be self-contained.
すべてのヘッダーファイルは自己完結、すなわち、それ単体でコンパイルできるようになっていなければなりません。
Users and refactoring
tools should not have to adhere to special conditions to include the
header.
ヘッダーファイルの利用者やリファクタリングツールに対して、そのヘッダーを#include
する際の事前条件を何ら課さないようにしてください。
Specifically, a header should
have header guards and include all
other headers it needs.
すなわち、ヘッダーにはインクルードガードを書き、そのヘッダーが必要とする他のヘッダーをすべて#include
してください。
When a header declares inline functions or templates that clients of the
header will instantiate, the inline functions and templates must also have
definitions in the header, either directly or in files it includes.
あるヘッダーファイルにおいて、利用者に向けたインライン関数やテンプレートを宣言するときは、それらの定義も、同一のヘッダーファイル内か、同一ヘッダーファイルから#include
するファイルの中に含めてください。
Do not move
these definitions to separately included header (-inl.h
) files;
this practice was common in the past, but is no longer allowed.
これらの定義を、別途#include
されるヘッダーファイル (-inl.h
) に移動してはいけません。この手法は、過去には慣習的でしたが、現在では許容されません。
When all
instantiations of a template occur in one .cc
file, either because
they're
explicit or because the definition is accessible to only
the .cc
file, the template definition can be kept in that file.
また、あるテンプレートのインスタンス化が特定の単一の.cc
ファイル内でのみ行われる場合には、そのテンプレートの定義を.cc
ファイルの中に保持してもかまいません。これは、たとえば明示的なテンプレートのインスタンス化を行う場合や、あるいは、テンプレートの定義へのアクセスをその.cc
ファイルからのみに限定している場合などがあてはまります。
There are rare cases where a file designed to be included is not
self-contained.
レアケースながら、#include
されることを目的としつつも、自己完結でないファイルも存在します。
These are typically intended to be included at unusual
locations, such as the middle of another file.
これらのファイルは、典型的には、別のファイルの中ほどなどの特殊な場所で#include
されることを意図して作られており、
They might not
use header guards, and might not include their prerequisites.
その内容としてインクルードガードを持っていなかったり、そのヘッダーが必要とする他のファイルを#include
していなかったりします。
Name such files with the .inc
extension.
このようなファイルには、.inc
拡張子で終わるファイル名をつけてください。
Use sparingly, and prefer self-contained headers when possible.
ただし、なるべく使用するのを控え、可能な限り自己完結ヘッダーを用いるようにしてください。
インクルードガード
All header files should have #define
guards to
prevent multiple inclusion.
多重にインクルードされるのを防ぐために、すべてのヘッダーファイルにおいて、インクルードガード(#define
によるガード)を持たせます。
The format of the symbol name
should be
<PROJECT>_<PATH>_<FILE>_H_
.
ガードのシンボル名は<PROJECT>_<PATH>_<FILE>_H_
の形式とします。
To guarantee uniqueness, they should
be based on the full path in a project's source tree.
ユニーク性を保証するため、インクルードガードのシンボル名は、プロジェクト内ソースツリー上のフルパスに基づくものをつけてください。
For example, the file foo/src/bar/baz.h
in
project foo
should have the following
guard:
たとえば、プロジェクトfooに含まれるファイルfoo/src/bar/baz.h
のインクルードガードは次のようにします。
#ifndef FOO_BAR_BAZ_H_ #define FOO_BAR_BAZ_H_ ... #endif // FOO_BAR_BAZ_H_
必要なものを#include
する
If a source or header file refers to a symbol defined elsewhere,
the file should directly include a header file which properly intends
to provide a declaration or definition of that symbol.
ソースファイル上やヘッダーファイル上において、何らか他の場所で定義されたシンボルを参照したいときは、そのシンボルの宣言・定義の提供を意図されたヘッダーファイルを直接#include
します。
It should not
include header files for any other reason.
そして、それ以外の理由では#include
してはいけません。
Do not rely on transitive inclusions.
推移的な#include
に頼ってはいけません。
This allows people to remove
no-longer-needed #include
statements from their headers without
breaking clients.
推移的な#include
が禁止されているのであればこそ、ヘッダーファイルの著者は、ユーザーのコードを破壊することなく、安心して、不要となった#include
文を削除できるようになります。
This also applies to related headers
- foo.cc
should include bar.h
if it uses a
symbol from it even if foo.h
includes bar.h
.
なお、このルールは、先の「.cc
に対応するヘッダー」にも適用されます。
foo.cc
でbar.h
のシンボルを使うのであれば、たとえ既にfoo.h
で#include "bar.h"
していたとしても、改めてfoo.cc
でも#include "bar.h"
してください。
前方宣言
Avoid using forward declarations where possible.
前方宣言は、可能な限り避けます。
Instead, include the headers you need.
かわりに必要なヘッダーを#include
します。
A "forward declaration" is a declaration of an entity without an associated definition. 「前方宣言」とは、対応する定義のないエンティティの宣言のことです。
// In a C++ source file: class B; void FuncInB(); extern int variable_in_b; ABSL_DECLARE_FLAG(flag_in_b);
// C++ソース内: class B; void FuncInB(); extern int variable_in_b; ABSL_DECLARE_FLAG(flag_in_b);
-
Forward declarations can save compile time, as
#include
s force the compiler to open more files and process more input. 前方宣言を用いると、コンパイラが#include
によって多数のファイルを開いて処理する分のコンパイル時間を節約できます。 -
Forward declarations can save on unnecessary
recompilation.
前方宣言は、余計な再コンパイルを抑えることができます。
#include
s can force your code to be recompiled more often, due to unrelated changes in the header. 前方宣言のかわりに#include
していた場合、ヘッダーの変更がコードに無関係であっても、ソースが再コンパイルされ、コンパイル頻度が上がります。
- Forward declarations can hide a dependency, allowing user code to skip necessary recompilation when headers change. 前方宣言によって、依存性が隠されてしまうことがあります。ヘッダーが更新され、本来はコードの再コンパイルが必要となっているにもかかわらず、それをスキップさせてしまうことがあります。
-
A forward declaration as opposed to an
#include
statement makes it difficult for automatic tooling to discover the module defining the symbol. 前方宣言を用いると、単に#include
した場合に比べて、自動化ツールがシンボル定義を見つけ出すのを困難にする場合があります。 - A forward declaration may be broken by subsequent changes to the library. 前方宣言は、ライブラリ側の将来の変更によって壊れてしまう危険性があります。 Forward declarations of functions and templates can prevent the header owners from making otherwise-compatible changes to their APIs, such as widening a parameter type, adding a template parameter with a default value, or migrating to a new namespace. 関数やテンプレートの前方宣言は、ヘッダーのメンテナによる、本来互換性を保てるはずのAPIの変更の妨げとなる場合があります。これらの前方宣言があると、たとえば、パラメータの型を広くしたり、デフォルト値とともにテンプレートパラメータを追加したり、あるいは、新しい名前空間へ移行したりすることが難しくなります。
-
Forward declaring symbols from namespace
std::
yields undefined behavior.std::
名前空間内のシンボルに対する前方宣言は未定義動作です。 -
It can be difficult to determine whether a forward
declaration or a full
#include
is needed. そもそも、前方宣言だけで済むのか、あるいは、#include
を必要とするのか、判断すること自体が困難です。 Replacing an#include
with a forward declaration can silently change the meaning of code: たとえば、次のコードの#include
を単純に前方宣言に置き換えたとすると、コードの意味が変わってしまいます。If the// b.h: struct B {}; struct D : B {}; // good_user.cc: #include "b.h" void f(B*); void f(void*); void test(D* x) { f(x); } // Calls f(B*)
// b.h: struct B {}; struct D : B {}; // good_user.cc: #include "b.h" void f(B*); void f(void*); void test(D* x) { f(x); } // f(B*)を呼び出す
#include
was replaced with forward decls forB
andD
,test()
would callf(void*)
. 上のコード例において、もし、#include
をB
とD
に対する前方宣言に置き換えてしまうと、それらの間の継承関係が見えなくなり、test(D* x)
はf(void*)
を呼び出すでしょう。 -
Forward declaring multiple symbols from a header
can be more verbose than simply
#include
ing the header. 1つのヘッダーにある複数のシンボルを前方宣言するのは、単にそのヘッダーを#include
するよりも冗長になります。 - Structuring code to enable forward declaration (e.g., using pointer members instead of object members) can make the code slower and more complex. 前方宣言を用いると、そのために、コードが回りくどくなる場合があります (たとえば、クラスメンバをオブジェクトでは持てず、ポインタにする必要が生じます)。これによって、コードの実行が遅くなったり、あるいは、コード自体も、より複雑になったりしてしまいます。
- Try to avoid forward declarations of entities defined in another project. プロジェクト外で定義されるエンティティの前方宣言は避けましょう。
インライン関数
Define functions inline only when they are small, say, 10 lines or fewer. インライン関数は、関数定義が小さいとき(10行以下程度)に限ります。
You can declare functions in a way that allows the compiler to expand them inline rather than calling them through the usual function call mechanism. 関数は、通常の関数呼び出しのかわりに、コンパイラによって直接その場にインライン展開されることを許す形でも宣言することができます。
Inlining a function can generate more efficient object code, as long as the inlined function is small. 関数が十分に小さいとき、インライン化によって、より効率的なオブジェクトコードを生成できます。 Feel free to inline accessors and mutators, and other short, performance-critical functions. たとえば、getter関数(原: accessors)、setter関数(原: mutators)、あるいは、その他の短く、かつパフォーマンスクリティカルな関数は自由にインライン化してかまいません。
Overuse of inlining can actually make programs slower. インライン関数を濫用すると、逆にプログラムの動作が遅くなってしまうことがあります。 Depending on a function's size, inlining it can cause the code size to increase or decrease. 関数をインライン化すると、対象の関数のサイズによって、インライン化後のコードサイズが増減します。 Inlining a very small accessor function will usually decrease code size while inlining a very large function can dramatically increase code size. 非常に小さなgetter関数などをインライン化する分には、通常はコード全体のサイズが小さくなりますが、しかし、非常に大きな関数をインライン化してしまうと、コード全体のサイズが劇的に大きくなってしまうことがあります。 On modern processors smaller code usually runs faster due to better use of the instruction cache. 現代的なCPUにおいては、一般に、コードサイズが小さい方が、命令キャッシュを有効に使えるため、高速に動作します。
A decent rule of thumb is to not inline a function if it is more than 10 lines long. 経験則として、関数が10行より長くなったらインライン化すべきではありません。 Beware of destructors, which are often longer than they appear because of implicit member- and base-destructor calls! また、デストラクタには特に注意が必要です。デストラクタは、暗黙的にメンバのデストラクタと基底クラスのデストラクタを呼び出すため、その見た目に比べて長い実体を持つことがあります。
Another useful rule of thumb: it's typically not cost effective to inline functions with loops or switch statements (unless, in the common case, the loop or switch statement is never executed). もうひとつの経験則として、ループやswitch文を含む関数は、(それらの文がまったく実行されないような場合を除いて)大抵の場合、インライン化しても効率的になりません。
It is important to know that functions are not always inlined even if they are declared as such; for example, virtual and recursive functions are not normally inlined. なお、注意点として、関数をインラインに宣言したとしても、あくまでインライン化を許すだけであって、必ずインライン化されるわけではありません。たとえば、仮想関数や再帰関数は通常はインライン化されません。 Usually recursive functions should not be inline. 通常、再帰関数はインラインにすべきではありません。 The main reason for making a virtual function inline is to place its definition in the class, either for convenience or to document its behavior, e.g., for accessors and mutators. 仮想関数をクラス内にインライン定義する主な理由は、利便性や、あるいは、(たとえば、getterやsetterなどの場合に)その挙動に関するドキュメントとしての役割を持たせられることにあります。
#include
の名前と順序
Include headers in the following order: Related header, C system headers,
C++ standard library headers,
other libraries' headers, your project's
headers.
#include
は次の順番で行います: .cc
自身と対応するヘッダー、C言語のシステムヘッダー、C++標準ライブラリヘッダー、その他のライブラリのヘッダー、自分のプロジェクト内のヘッダー。
All of a project's header files should be
listed as descendants of the project's source
directory without use of UNIX directory aliases
.
(the current directory) or ..
(the parent directory).
すべてのプロジェクトヘッダーのパスは、プロジェクトのソースディレクトリのルートからの相対パスで記述します。その際、UNIXディレクトリのエイリアスである./
(カレントディレクトリ)や、../
(親ディレクトリ)は省きます。
For example,
google-awesome-project/src/base/logging.h
should be included as:
たとえば、google-awesome-project/src/base/logging.h
の#include
文は次のようにします。
#include "base/logging.h"
Headers should only be included using an angle-bracketed path if the library
requires you to do so.
<>
を使ってヘッダーをインクルードするのは、使用するライブラリがそれを必要とする場合に限定します。
In particular, the following headers require angle
brackets:
特に、次のようなヘッダーでは<>
を必要とします。
-
C and C++ standard library headers (e.g.
<stdlib.h>
and<string>
). C言語標準ライブラリ および C++標準ライブラリのヘッダー(例えば<stdlib.h>
や<string>
など)。 -
POSIX, Linux, and Windows system headers (e.g.
<unistd.h>
and<windows.h>
). POSIX, Linux, Windows 等のシステムヘッダー (例えば<unistd.h>
や<windows.h>
など)。 -
In rare cases, third_party libraries (e.g.
<Python.h>
). まれですが、サードパーティライブラリのヘッダー(例えば<Python.h>
など)。
In dir/foo.cc
or
dir/foo_test.cc
, whose main
purpose is to implement or test the stuff in
dir2/foo2.h
, order your includes
as follows:
#include
の順序の例として、たとえばdir/foo.h
の宣言を実装するファイルdir/foo.cc
や、もしくは、それらをテストする目的のファイルdir/foo_test.cc
においては、次の順に並べます。
-
dir2/foo2.h
.dir/foo.h
(対応するヘッダーファイル) - A blank line 空行
-
C system headers, and any other headers in angle brackets with the
.h
extension, e.g.,<unistd.h>
,<stdlib.h>
,<Python.h>
. C言語のシステムヘッダーと、その他の<>
で囲まれる拡張子.h
を持つもの、たとえば<unistd.h>
や<stdlib.h>
、<Python.h>
など - A blank line 空行
-
C++ standard library headers (without file extension), e.g.,
<algorithm>
,<cstddef>
. C++標準ライブラリのヘッダー(拡張子のないもの)、たとえば<algorithm>
や<cstddef>
など - A blank line 空行
-
Other libraries' .h files.
他のライブラリの
.h
ファイル - A blank line 空行
-
Your project's
.h
files. 自分のプロジェクトの.hファイル
Separate each non-empty group with one blank line. 空でないグループ同士の間には空行を1つ入れます。
With the preferred ordering, if the related header
dir2/foo2.h
omits any necessary
includes, the build of dir/foo.cc
or dir/foo_test.cc
will break.
この順番にしておくと、.cc
の対応ヘッダーdir/foo.h
で必要なインクルードを忘れてしまったときに、まずdir/foo.cc
やdir/foo_test.cc
のコンパイルが失敗します。
Thus, this rule ensures that build breaks show up first
for the people working on these files, not for innocent
people in other packages.
これによって、ビルドが壊れたときに、他のパッケージで作業している無実の人ではなく、当該ファイルで作業をしていた本人が最初に気づくことができるようになります。
dir/foo.cc
and
dir2/foo2.h
are usually in the same
directory (e.g., base/basictypes_test.cc
and
base/basictypes.h
), but may sometimes be in different
directories too.
dir/foo.cc
とdir/foo.h
は、通常は同じディレクトリ内に置きます(たとえば、base/basictypes_test.cc
とbase/basictypes.h
も同様です)が、必要に応じて異なるディレクトリに置いてもかまいません。
Note that the C headers such as stddef.h
are essentially interchangeable with their C++ counterparts
(cstddef
).
なお、stddef.h
のようなC言語のヘッダーは、C++の相当するヘッダー(cstddef
)と相互に置き換えることができます。
Either style is acceptable, but prefer consistency with existing code.
どちらのスタイルでもかまいませんが、既存のコードとの一貫性を優先してください。
Within each section the includes should be ordered alphabetically. 各セクション内ではアルファベット順に並べます。 Note that older code might not conform to this rule and should be fixed when convenient. 古いコードではそうなっていないことがありますが、修正したほうが都合がよければ、修正してしまいましょう。
For example, the includes in
google-awesome-project/src/foo/internal/fooserver.cc
might look like this:
全体の例として、たとえば、google-awesome-project/src/foo/internal/fooserver.cc
の#include
は次のようになります。
#include "foo/server/fooserver.h" #include <sys/types.h> #include <unistd.h> #include <string> #include <vector> #include "base/basictypes.h" #include "foo/server/bar.h" #include "third_party/absl/flags/flag.h"
Sometimes, system-specific code needs
conditional includes.
システム特有のコードは、しばしば条件付きの#include
を必要とする場合があります。
Such code can put conditional
includes after other includes.
そのような条件付き#include
は、それ以外の#include
の後に配置します。
Of course, keep your
system-specific code small and localized.
もちろんのことですが、システム特有のコードは小さく局所的に保つようにしてください。
Example:
次に例を示します。
#include "foo/public/fooserver.h" #include "base/port.h" // For LANG_CXX11. #ifdef LANG_CXX11 #include <initializer_list> #endif // LANG_CXX11
スコープ
名前空間
With few exceptions, place code in a namespace.
一部の例外を除いて、コードは何らかの名前空間の中に置きます。
Namespaces should have unique names based on the project name, and possibly its path.
名前空間にはプロジェクト名やファイルパスに基づくユニークな名前をつけます。
Do not use using-directives (e.g.,
using namespace foo
).
usingディレクティブ(using namespace foo
)は使ってはいけません。
Do not use inline namespaces.
インライン名前空間を使ってはいけません。
For unnamed namespaces, see
Internal Linkage.
無名名前空間については内部リンケージを参照してください。
Namespaces subdivide the global scope into distinct, named scopes, and so are useful for preventing name collisions in the global scope. 名前空間によって、グローバルスコープを異なる名前付きのスコープに分割できます。これによって、グローバルスコープにおける名前の衝突を防ぐことができます。
Namespaces provide a method for preventing name conflicts in large programs while allowing most code to use reasonably short names. 名前空間を用いると、大きなプログラムであっても、コードの大部分において合理的な短い名前を使いながら、全体として名前の衝突を防ぐことができます。
For example, if two different projects have a class
Foo
in the global scope, these symbols may
collide at compile time or at runtime.
たとえば、2つの異なるプロジェクトにおいて同じ名前のクラスFoo
がグローバル名前空間に含まれていたとすると、これらのシンボルはコンパイル時や実行時に衝突してしまいます。
If each project
places their code in a namespace, project1::Foo
and project2::Foo
are now distinct symbols that
do not collide, and code within each project's namespace
can continue to refer to Foo
without the prefix.
もし、それぞれのプロジェクトがそれらのクラスをプロジェクト独自の名前空間の中に入れていたならば、project1::Foo
とproject2::Foo
とは区別できるようになり、衝突することはなくなります。しかも、各プロジェクトの名前空間内では、引き続き、接頭辞なしのただのFoo
という名前で、そのクラスを参照することができます。
Inline namespaces automatically place their names in the enclosing scope. インライン名前空間は、自身の名前を自動的に外側のスコープに配置します。 Consider the following snippet, for example: 例として、次のスニペットについて考えます。
namespace outer { inline namespace inner { void foo(); } // namespace inner } // namespace outer
The expressions outer::inner::foo()
and
outer::foo()
are interchangeable.
このとき、式outer::inner::foo()
とouter::foo()
とは相互に交換可能です。
Inline namespaces are primarily intended for ABI compatibility across versions.
インライン名前空間は、主にバージョン間におけるABIの互換性を保つことを目的として利用することができます。
Namespaces can be confusing, because they complicate the mechanics of figuring out what definition a name refers to. 名前空間は、名前から定義を解決する仕組みをより複雑化させるため、困惑を招くことがあります。
Inline namespaces, in particular, can be confusing because names aren't actually restricted to the namespace where they are declared. 特にインライン名前空間は、事実上、その中で宣言される名前を名前空間の中に閉じ込めないため困惑の元となりやすいです。 They are only useful as part of some larger versioning policy. インライン名前空間は、バージョン管理ポリシーの一部としてのみ役に立ちます。
In some contexts, it's necessary to repeatedly refer to symbols by their fully-qualified names. また、文脈によっては、完全修飾された名前を用いて繰り返しシンボルを参照する必要に迫られることがあります。 For deeply-nested namespaces, this can add a lot of clutter. このとき、名前空間のネストが深いと、コードが散らかってしまいます。
Namespaces should be used as follows: 名前空間は次のように使いましょう。
- Follow the rules on Namespace Names 名前空間の名前のルールに従ってください。
- Terminate multi-line namespaces with comments as shown in the given examples. 複数行からなる名前空間の終わりには、先ほどの例の通り、コメントをつけてください。
-
Namespaces wrap the entire source file after
includes,
gflags definitions/declarations
and forward declarations of classes from other namespaces.
名前空間は、
include
文、gflagsの定義や宣言、他の名前空間のクラスの前方宣言の後に続く、すべてのソースコードを包むようにしてください。// In the .h file namespace mynamespace { // All declarations are within the namespace scope. // Notice the lack of indentation. class MyClass { public: ... void Foo(); }; } // namespace mynamespace
// .h ファイル namespace mynamespace { // すべての宣言は名前空間のスコープに含めます。 // 名前空間ではインデントしないことに注意してください。 class MyClass { public: ... void Foo(); }; } // namespace mynamespace
// In the .cc file namespace mynamespace { // Definition of functions is within scope of the namespace. void MyClass::Foo() { ... } } // namespace mynamespace
// .ccファイル namespace mynamespace { // 関数の定義は名前空間に含めます。 void MyClass::Foo() { ... } } // namespace mynamespace
.cc
files might have additional details, like flags or using-declarations. より複雑な.cc
ファイルでは、flagsやusing宣言などを含む場合もあるかもしれません。#include "a.h" ABSL_FLAG(bool, someflag, false, "a flag"); namespace mynamespace { using ::foo::Bar; ...code for mynamespace... // Code goes against the left margin. } // namespace mynamespace
#include "a.h" ABSL_FLAG(bool, someflag, false, "a flag"); namespace mynamespace { using ::foo::bar; ...code for mynamespace... // コードの左側には余白を入れません。 } // namespace mynamespace
-
To place generated protocol
message code in a namespace, use the
package
specifier in the.proto
file. Protocol Buffer が生成したメッセージコードを名前空間に含めたいときは、.proto
ファイル内のpackage
指定子を使ってください。 See Protocol Buffer Packages for details. 詳細は Protocol Buffer Packages を参照してください。 -
Do not declare anything in namespace
std
, including forward declarations of standard library classes.std
名前空間の中には、標準ライブラリの前方宣言を含めて、何も宣言してはいけません。 Declaring entities in namespacestd
is undefined behavior, i.e., not portable.std
名前空間の中に何かを宣言することは未定義動作であり、コードの移植性を失ってしまいます。 To declare entities from the standard library, include the appropriate header file. 標準ライブラリのエンティティを宣言する目的では、適切なヘッダーファイルを#include
してください。 -
You may not use a using-directive to make all names from a namespace available. 名前空間内のすべての名前を利用できるようにするためのusingディレクティブは使ってはいけません。
// Forbidden -- This pollutes the namespace. using namespace foo;
// 不可。名前空間を汚染している。 using namespace foo;
-
Do not use Namespace aliases at namespace scope in header files except in explicitly marked internal-only namespaces, because anything imported into a namespace in a header file becomes part of the public API exported by that file. 内部用であることを明示した名前空間の中を除き、名前空間のエイリアス(Namespace aliase)をヘッダーファイルの名前空間スコープで宣言してはいけません。このようなヘッダーファイルで名前空間にインポートされたすべてのものは、そのヘッダーファイルが提供するAPIの一部として公開したことになります。
// Shorten access to some commonly used names in .cc files. namespace baz = ::foo::bar::baz;
// .ccファイル内 // たくさん使われる名前へのショートカットを作る。 namespace baz = ::foo::bar::baz;
// Shorten access to some commonly used names (in a .h file). namespace librarian { namespace internal { // Internal, not part of the API. namespace sidetable = ::pipeline_diagnostics::sidetable; } // namespace internal inline void my_inline_function() { // namespace alias local to a function (or method). namespace baz = ::foo::bar::baz; ... } } // namespace librarian
// .hファイル内 // よく使われる名前へのショートカットを作る。 namespace librarian { namespace internal { // 内部用。APIではありません。 namespace sidetable = ::pipeline_diagnostics::sidetable; } // namespace internal inline void my_inline_function() { // 関数内に限定された名前空間の別名。 namespace baz = ::foo::bar::baz; ... } } // namespace librarian
- Do not use inline namespaces. インライン名前空間を使ってはいけません。
Use namespaces with "internal" in the name to document parts of an API that should not be mentioned by users of the API. APIのユーザーによって触れられるべきでない部分は、その旨を明示するために"internal"を含めた名前の名前空間を使ってください。
// We shouldn't use this internal name in non-absl code. using ::absl::container_internal::ImplementationDetail;
// absl外のコードでは、abslのinternalな名前を使用すべきではありません。 using ::absl::container_internal::ImplementationDetail;
Single-line nested namespace declarations are preferred in new code, but are not required. 新しく書くコードにおいては、1行でネストした名前空間宣言を用いるのが望ましいですが、必須ではありません。
内部リンケージ
When definitions in a .cc
file do not need to be
referenced outside that file, give them internal linkage by placing
them in an unnamed namespace or declaring them static
.
エンティティの定義が.cc
ファイル内にあり、かつ、ファイル外から参照される必要がないときは、それらを無名の名前空間内に置くか、もしくはstatic
に宣言することによって、内部リンケージとなるようにします。
Do not use either
of these constructs in .h
files.
なお、.h
ファイル内では、これらのいずれも行ってはいけません。
All declarations can be given internal linkage by placing them in unnamed
namespaces.
無名名前空間内で宣言を行うことによって、それらを内部リンケージとすることができます。
Functions and variables can also be given internal linkage by
declaring them static
.
関数と変数は、static
に宣言することでも、内部リンケージにすることができます。
This means that anything you're declaring
can't be accessed from another file.
内部リンケージのエンティティは他のファイルからアクセスできなくなります。
If a different file declares something with
the same name, then the two entities are completely independent.
また、仮に、異なるファイル間において同名のエンティティが宣言されていたとしても、それらは完全に独立した別の実体として扱われることになります。
Use of internal linkage in .cc
files is encouraged
for all code that does not need to be referenced elsewhere.
他の場所から参照される必要のないすべてのコードを、.cc
内の内部リンケージにすることを推奨します。
Do not use internal linkage in .h
files.
.h
ファイルでは内部リンケージは使用してはいけません。
Format unnamed namespaces like named namespaces. 無名名前空間も、名前付きの名前空間と同じようにフォーマットしてください。 In the terminating comment, leave the namespace name empty: 無名名前空間が終わるときのコメントは、次のように、名前空間名を空白のままにしたものを記述してください。
namespace { ... } // namespace
非メンバ関数、静的メンバ関数、グローバル関数
Prefer placing nonmember functions in a namespace; use completely global functions rarely. 非メンバ関数(すなわち通常の関数)は何らかの名前空間内に置きます。完全なグローバル関数はまず使いません。 Do not use a class simply to group static members. 静的メンバを単にグループ化する目的でクラスを作ってはいけません。 Static methods of a class should generally be closely related to instances of the class or the class's static data. 静的メンバ関数は、そのクラスのインスタンスや静的データと強い関連がある場合に限って使用します。
Nonmember and static member functions can be useful in some situations. 非メンバ関数や静的メンバ関数は、様々な場面で有用です。 Putting nonmember functions in a namespace avoids polluting the global namespace. 非メンバ関数は何らかの名前空間内に置くことで、グローバル名前空間の汚染を避けられます。
Nonmember and static member functions may make more sense as members of a new class, especially if they access external resources or have significant dependencies. 非メンバ関数や静的メンバ関数は、新しいクラスのメンバとした方が合理的な場合があります。特に、それらが外部のリソースにアクセスしていたり、あるいは強く依存していたりする場合などです。
Sometimes it is useful to define a function not bound to a class instance. 場合によって、いずれのクラスインスタンスにも紐付かない関数を定義する方が都合が良いことがあります。 Such a function can be either a static member or a nonmember function. このような関数は、静的メンバ関数や非メンバ関数(通常の関数)のいずれかとすることができます。 Nonmember functions should not depend on external variables, and should nearly always exist in a namespace. 非メンバ関数を定義する場合、それらは外部の変数に依存すべきではありません。また、ほとんど常に名前空間の中におかれなければなりません。 Do not create classes only to group static members; this is no different than just giving the names a common prefix, and such grouping is usually unnecessary anyway. 単に静的なメンバをグループ化するためだけの目的でクラスを作ってはいけません。 このようなクラスを作ることは、名前に共通の接頭辞をつけることと何ら違いがありませんし、そもそも、通常はそのようなグループ化を行うこと自体が不要なはずです。
If you define a nonmember function and it is only
needed in its .cc
file, use
internal linkage to limit
its scope.
また、非メンバ関数を定義する場合で、かつ、その関数が特定の.cc
ファイル内からのみ必要とされる場合には、そのスコープを制限するために内部リンケージを使用してください。
ローカル変数
Place a function's variables in the narrowest scope possible, and initialize variables in the declaration. 関数内の変数宣言は、可能な限りそのスコープを狭くします。また、変数は宣言と同時に初期化します。
C++ allows you to declare variables anywhere in a function. C++では、関数内のどこでも変数を宣言することができます。 We encourage you to declare them in a scope as local as possible, and as close to the first use as possible. 変数は、できる限り局所的なスコープの中で、また、できる限り初めて使う場所の近くで宣言することが望ましいです。 This makes it easier for the reader to find the declaration and see what type the variable is and what it was initialized to. そうしておくことで、コードの読者が変数宣言を見つけやすくなり、変数の型や初期化の方法についての見通しがよくなります。 In particular, initialization should be used instead of declaration and assignment, e.g.,: また、変数は、変数宣言と代入を別々に行うのではなく、宣言と同時に初期化するようにしてください。以下に例を示します。
int i; i = f(); // Bad -- initialization separate from declaration.
int i; i = f(); // 悪い例。初期化が宣言と分かれています。
int i = f(); // Good -- declaration has initialization.
int i = f(); // 良い例。宣言と同時に初期化が行われています。
int jobs = NumJobs(); f(jobs); // Good -- declaration immediately (or closely) followed by use.
int jobs = NumJobs(); f(jobs); // 良い例。使用する直前に(あるいはすぐ近くで)宣言されています。
std::vector<int> v; v.push_back(1); // Prefer initializing using brace initialization. v.push_back(2);
std::vector<int> v; v.push_back(1); // {}による初期化を使う方が望ましいです。 v.push_back(2);
std::vector<int> v = {1, 2}; // Good -- v starts initialized.
std::vector<int> v = {1, 2}; // 良い例。宣言と同時に初期化されています。
Variables needed for if
, while
and for
statements should normally be declared
within those statements, so that such variables are confined
to those scopes. E.g.,:
if
文やwhile
文、for
文で使用される変数は、通常はそれらの文中で宣言します。そうすることで、変数のスコープを制限できます。
以下に例を示します。
while (const char* p = strchr(str, '/')) str = p + 1;
There is one caveat: if the variable is an object, its constructor is invoked every time it enters scope and is created, and its destructor is invoked every time it goes out of scope. ただし、変数がオブジェクト型であるときは少し注意が必要です。スコープ内で変数を宣言した場合、スコープに入るたびに毎回コンストラクタが呼ばれ、オブジェクトが生成され、スコープから出るたびに毎回デストラクタが呼ばれることになります。
// Inefficient implementation: for (int i = 0; i < 1000000; ++i) { Foo f; // My ctor and dtor get called 1000000 times each. f.DoSomething(i); }
// 非効率的な実装 for (int i = 0; i < 1000000; ++i) { Foo f; // コンストラクタとデストラクタがそれぞれ 1000000 回呼ばれます。 f.DoSomething(i); }
It may be more efficient to declare such a variable used in a loop outside that loop: このような場合、変数宣言をループの外側に出すことで、より効率的にできる場合があります。
Foo f; // My ctor and dtor get called once each. for (int i = 0; i < 1000000; ++i) { f.DoSomething(i); }
Foo f; // コンストラクタとデストラクタが1回だけ呼ばれます。 for (int i = 0; i < 1000000; ++i) { f.DoSomething(i); }
静的変数とグローバル変数
Objects with static storage duration are forbidden unless they are trivially destructible. 「静的記憶域期間(static storage duration)」を持つオブジェクトは、それらが「自明に破壊可能(trivially destructible)」でない限り禁止します。 Informally this means that the destructor does not do anything, even taking member and base destructors into account. 「自明に破壊可能」とは、ざっくり言うと、そのメンバ変数や基底クラスのデストラクタまで含めて、デストラクタが一切何もしないということを意味します。 More formally it means that the type has no user-defined or virtual destructor and that all bases and non-static members are trivially destructible. より正式には、型にユーザー定義デストラクタまたは仮想デストラクタがなく、かつ、すべての基底クラスと非静的メンバもまた同様に自明に破壊可能であるときに「自明に破壊可能」が成り立ちます。 Static function-local variables may use dynamic initialization. 関数ローカルスコープの静的変数は、動的初期化を行ってもかまいません。 Use of dynamic initialization for static class member variables or variables at namespace scope is discouraged, but allowed in limited circumstances; see below for details. それ以外の、クラススコープの静的メンバ変数や名前空間スコープの静的変数については、動的初期化を行うことは原則として非推奨です。ただし、後述するいくつかの限定された状況においては、使用が認められることもあります。
As a rule of thumb: a global variable satisfies these requirements if its
declaration, considered in isolation, could be constexpr
.
大雑把な判断基準として、グローバル変数については、その宣言が他に独立してconstexpr
にできるとき、これらの要件を満たしていると言えます。
Every object has a storage duration, which correlates with its
lifetime.
すべてのオブジェクトには記憶域期間(storage duration)があり、その寿命と関連しています。
Objects with static storage duration live from the point of their
initialization until the end of the program.
静的記憶域期間を持つオブジェクトは、それらが初期化された時点からプログラムの終了までの寿命を持ちます。
Such objects appear as variables at
namespace scope ("global variables"), as static data members of classes, or as
function-local variables that are declared with the static
specifier.
これらのオブジェクトは、名前空間スコープの変数(いわゆるグローバル変数)や、クラスの静的データメンバ変数、あるいは、static
指定子つきで宣言される関数ローカル変数として現れます。
Function-local static variables are initialized when control first
passes through their declaration; all other objects with static storage duration
are initialized as part of program start-up.
関数ローカルの静的変数は、プログラムの処理が初めてその宣言を通過するときに初期化されますが、それ以外の静的オブジェクトは、プログラムの起動処理の一環として初期化されることになります。
All objects with static storage
duration are destroyed at program exit (which happens before unjoined threads
are terminated).
静的記憶域期間を持つすべてのオブジェクトは、プログラムの終了時に破壊されます。この破壊処理は、プログラムの終了時に待機(join)されていないスレッドが存在する場合、それらのスレッドが強制終了されるよりも前に行われるため、注意が必要です。
Initialization may be dynamic, which means that something non-trivial happens during initialization. これらの変数は動的(dynamic)に初期化することもできますが、そのような初期化においては自明ではないことが起こるかもしれません。 (For example, consider a constructor that allocates memory, or a variable that is initialized with the current process ID.) (たとえば、メモリを割り当てるコンストラクタの実行や、現在のプロセスIDによって初期化される変数などがこれに当てはまります。) The other kind of initialization is static initialization. また、これとは別の初期化の種類として静的(static)初期化があります。 The two aren't quite opposites, though: static initialization always happens to objects with static storage duration (initializing the object either to a given constant or to a representation consisting of all bytes set to zero), whereas dynamic initialization happens after that, if required. これら2つの初期化は完全に相反するわけではありません。 静的初期化は、すべての静的記憶域期間を持つオブジェクトにおいて必ず発生します。(オブジェクトは、初期化子に与えられた何らかの定数や、あるいはゼロで埋めることによって初期化されます。) そして、その後で、必要に応じて動的初期化が行われます。
Global and static variables are very useful for a large number of applications: named constants, auxiliary data structures internal to some translation unit, command-line flags, logging, registration mechanisms, background infrastructure, etc. グローバル変数と静的変数は様々な用途で有用です。その用途は、たとえば、名前付きの定数、翻訳単位内部の補助的データ構造、コマンドラインフラグ、ログ出力、登録メカニズム、バックグラウンドインフラストラクチャなど、多岐にわたり、多数のアプリケーションで非常に役立ちます。
Global and static variables that use dynamic initialization or have non-trivial destructors create complexity that can easily lead to hard-to-find bugs. 動的初期化や非自明(non-trivial)なデストラクタを持つグローバル変数や静的変数は、プログラムを複雑化させ、容易に発見困難なバグを引き起こします。 Dynamic initialization is not ordered across translation units, and neither is destruction (except that destruction happens in reverse order of initialization). 静的記憶域期間を持つ変数の動的初期化は、翻訳単位間をまたいだ順序付けが行われません。また、それらが破壊される順序も(初期化の逆順で行われるという点を除けば)同様です。 When one initialization refers to another variable with static storage duration, it is possible that this causes an object to be accessed before its lifetime has begun (or after its lifetime has ended). このため、ある変数の初期化処理(あるいは破壊処理)において、他の静的記憶域期間を持つ変数を参照していると、そのオブジェクトが初期化される前(あるいは寿命が終わった後)にそのオブジェクトにアクセスしてしまう恐れがあります。 Moreover, when a program starts threads that are not joined at exit, those threads may attempt to access objects after their lifetime has ended if their destructor has already run. さらに、プログラムが実行中に何らかのスレッドを起動していて、プログラム終了時にそれらを待機(join)しなかった場合、それらのスレッドから、既にデストラクタが実行され寿命が終わったオブジェクトへのアクセスを試みてしまうかもしれません。
破壊に関する結論
When destructors are trivial, their execution is not subject to ordering at
all (they are effectively not "run"); otherwise we are exposed to the risk of
accessing objects after the end of their lifetime.
デストラクタが自明(trivial)な場合、それらの実行は順序付けの議論の対象とはなりません (そもそも、これらのオブジェクトのデストラクタには、実質的に「実行される処理」がありません)。しかし、非自明なデストラクタを持つオブジェクトを静的変数とした場合には、潜在的に、寿命が終わった後のオブジェクトにアクセスしてしまう危険性を抱え込むことになります。
Therefore, we only allow
objects with static storage duration if they are trivially destructible.
したがって、オブジェクトを静的記憶域期間としても良い条件として、少なくともそれらが「自明に破壊可能(trivially destructible)」である場合にのみ認めることとします。
Fundamental types (like pointers and int
) are trivially
destructible, as are arrays of trivially destructible types.
基本的な組み込みの型(ポインタやint
など)は自明に破壊可能であり、自明に破壊可能な型の配列も同様に自明に破壊可能です。
Note that
variables marked with constexpr
are trivially destructible.
また、これらに加えてconstexpr
でマークされた変数も自明に破壊可能となりますので、この点は覚えておくとよいでしょう。
const int kNum = 10; // Allowed struct X { int n; }; const X kX[] = {{1}, {2}, {3}}; // Allowed void foo() { static const char* const kMessages[] = {"hello", "world"}; // Allowed } // Allowed: constexpr guarantees trivial destructor. constexpr std::array<int, 3> kArray = {1, 2, 3};
const int kNum = 10; // OK struct X { int n; }; const X kX[] = {{1}, {2}, {3}}; // OK void foo() { static const char* const kMessages[] = {"hello", "world"}; // OK } // OK: constexpr は trivial destructor を保証する。 constexpr std::array<int, 3> kArray = {1, 2, 3};
// Bad: non-trivial destructor const std::string kFoo = "foo"; // Bad for the same reason, even though kBar is a reference (the // rule also applies to lifetime-extended temporary objects). const std::string& kBar = StrCat("a", "b", "c"); void bar() { // Bad: non-trivial destructor. static std::map<int, int> kData = {{1, 0}, {2, 0}, {3, 0}}; }
// ダメ: non-trivial destructor const std::string kFoo = "foo"; // 同様の理由でダメ。kBarが参照であったとしても。 // このルールは、一時オブジェクトの有効期限の延長が行われた場合も対象。 const std::string& kBar = StrCat("a", "b", "c"); void bar() { // ダメ: non-trivial destructorを持つ static std::map<int, int> kData = {{1, 0}, {2, 0}, {3, 0}}; }
Note that references are not objects, and thus they are not subject to the
constraints on destructibility.
なお、参照はオブジェクトそのものではないため、破壊可能性に関する制約も受けません。
The constraint on dynamic initialization still
applies, though.
(ただし、その場合であっても、動的初期化に関する制約はそのまま適用されます。)
In particular, a function-local static reference of the form
static T& t = *new T;
is allowed.
特に、static T& t = *new T;
のような形の関数ローカルの静的参照変数は問題ありません。
初期化に関する結論
Initialization is a more complex topic. 初期化は、より複雑なトピックです。 This is because we must not only consider whether class constructors execute, but we must also consider the evaluation of the initializer: というのも、これは、クラスのコンストラクタが実行されるか否かに関してだけでなく、その初期化子の評価についても考える必要があるためです。
int n = 5; // Fine int m = f(); // ? (Depends on f) Foo x; // ? (Depends on Foo::Foo) Bar y = g(); // ? (Depends on g and on Bar::Bar)
int n = 5; // OK int m = f(); // ? (fに依存) Foo x; // ? (Fooのコンストラクタに依存) Bar y = g(); // ? (gとBarのコンストラクタに依存)
All but the first statement expose us to indeterminate initialization ordering. 最初の例を除くすべての文において、潜在的に初期化順序の不確定性に関する問題を抱えている可能性があります。
The concept we are looking for is called constant initialization in
the formal language of the C++ standard.
この議論で見いだそうとしている概念は、C++標準の形式言語において「定数初期化」と呼ばれる概念にあたります。
It means that the initializing
expression is a constant expression, and if the object is initialized by a
constructor call, then the constructor must be specified as
constexpr
, too:
定数初期化とは、初期化式が定数式であるということを意味します。
また、定数となるオブジェクトがコンストラクタ呼び出しによって初期化される場合には、そのコンストラクタもconstexpr
指定されていることも必要です。
struct Foo { constexpr Foo(int) {} }; int n = 5; // Fine, 5 is a constant expression. Foo x(2); // Fine, 2 is a constant expression and the chosen constructor is constexpr. Foo a[] = { Foo(1), Foo(2), Foo(3) }; // Fine
struct Foo { constexpr Foo(int) {} }; int n = 5; // OK、 5 は定数式。 Foo x(2); // OK、 2 は定数式で、選ばれるコンストラクタもconstexpr。 Foo a[] = { Foo(1), Foo(2), Foo(3) }; // OK
Constant initialization is always allowed.
定数初期化は、いつでも使ってかまいません。
Constant initialization of
static storage duration variables should be marked with constexpr
or constinit
.
静的記憶域期間を持つ変数を定数初期化するときは、常にconstexpr
かconstinit
でマークします。
Any non-local static storage
duration variable that is not so marked should be presumed to have
dynamic initialization, and reviewed very carefully.
そのようにマークされていない非ローカルな静的記憶域期間の変数は、動的初期化される可能性があるという前提のもと、非常に慎重なレビューを必要とします。
By contrast, the following initializations are problematic: これらの定数初期化とは対照的に、次の初期化には問題があります。
// Some declarations used below. time_t time(time_t*); // Not constexpr! int f(); // Not constexpr! struct Bar { Bar() {} }; // Problematic initializations. time_t m = time(nullptr); // Initializing expression not a constant expression Foo y(f()); // Ditto Bar b; // Chosen constructor Bar::Bar() not constexpr
// 以降の初期化時に使用される式の宣言 time_t time(time_t*); // constexprでない! int f(); // constexprでない! struct Bar { Bar() {} }; // 問題のある初期化 time_t m = time(nullptr); // 初期化式が定数式でない Foo y(f()); // 同上 Bar b; // 選択されるコンストラクタ Bar::Bar() がconstexprでない
Dynamic initialization of nonlocal variables is discouraged, and in general it is forbidden. ローカル変数以外を動的初期化することは非推奨であり、原則としては禁止です。 However, we do permit it if no aspect of the program depends on the sequencing of this initialization with respect to all other initializations. ただし、その個々の初期化について、プログラムのあらゆる側面からみて、その初期化と他のすべての初期化との間に、順序付けに関する一切の依存性がないと見なせる場合は、そのような初期化を行ってもかまいません。 Under those restrictions, the ordering of the initialization does not make an observable difference. For example: そのような制限の下においては、初期化の順序の違いが、観測可能な違いとして現れることはありません。例を示します。
int p = getpid(); // Allowed, as long as no other static variable // uses p in its own initialization.
int p = getpid(); // OK。ただし、他の静的変数の初期化において // pを使用していないときに限る。
Dynamic initialization of static local variables is allowed (and common). 一方で、ローカル静的変数については、それらを動的に初期化してかまいません (むしろ、これらの変数は、動的初期化されることが一般的です)。
よくあるパターン
-
Global strings: if you require a named global or static string constant,
consider using a
constexpr
variable ofstring_view
, character array, or character pointer, pointing to a string literal. グローバル文字列: 名前付きのグローバル文字列定数または静的文字列定数を必要とする場合は、constexpr
なstring_view
、constexpr
な文字配列、文字列リテラルを指すconstexpr
なポインタを使用することを検討してください。 String literals have static storage duration already and are usually sufficient. 文字列リテラルは、もともと静的記憶域期間を持ち、通常はそれで十分なことが多いです。 See TotW #140. TotW #140.を参照してください。 -
Maps, sets, and other dynamic containers: if you require a static, fixed
collection, such as a set to search against or a lookup table, you cannot
use the dynamic containers from the standard library as a static variable,
since they have non-trivial destructors.
連想配列(map)、集合(set)、およびその他の動的コンテナ: 検索対象集合やルックアップテーブルなどの用途で、静的かつ不変のコレクションが欲しいときであっても、標準ライブラリの動的コンテナは非自明なデストラクタ(non-trivial destructors)を持つため、これらを静的変数として使用してはいけません。
Instead, consider
a simple array of trivial types, e.g., an array of arrays of ints (for a "map from int
to
int"), or an array of pairs (e.g., pairs of
int
andconst char*
). かわりに、自明(trivial)な型の単純な配列、たとえばint
の配列の配列 (「int から int への連想配列」として)や、ペアの配列(たとえば、pair<int, const char*>[]
)等を検討してください。 For small collections, linear search is entirely sufficient (and efficient, due to memory locality); consider using the facilities from absl/algorithm/container.h for the standard operations. コレクションが小さいときは線形検索でまったく十分です (線形検索はメモリ局所性の面からみても効率的に動作します)。より一般的な操作を必要とする場合にはabsl/algorithm/container.hライブラリを採用することを検討してください。 If necessary, keep the collection in sorted order and use a binary search algorithm. また、必要に応じて、コレクションをソート済みの状態に保っておき、二分探索アルゴリズムを適用することもできるでしょう。 If you do really prefer a dynamic container from the standard library, consider using a function-local static pointer, as described below. それでも、どうしても標準ライブラリの動的コンテナを使いたいというときには、後述する関数ローカルな静的ポインタ変数を用いることを検討してください。 -
Smart pointers (
std::unique_ptr
,std::shared_ptr
): smart pointers execute cleanup during destruction and are therefore forbidden. スマートポインタ (std::unique_ptr
,std::shared_ptr
): スマートポインタはデストラクタ内で保持するオブジェクトの解放処理が実行されるため、静的変数としての使用は禁止します。 Consider whether your use case fits into one of the other patterns described in this section. このようなユースケースにおいては、このセクションで説明している他のパターンのいずれかに当てはめることができないか検討してください。 One simple solution is to use a plain pointer to a dynamically allocated object and never delete it (see last item). 簡単な解決策の1つは、単純な生のポインタ変数を用意し、動的に割り当てられたオブジェクトへのポインタを保持した上で、そのオブジェクトをdeleteしないままにすることです(最後の項目を参照)。 -
Static variables of custom types: if you require static, constant data of
a type that you need to define yourself, give the type a trivial destructor
and a
constexpr
constructor. 独自の型の静的変数: 独自に定義した型を静的定数データとして扱いたい場合は、その型に自明(trivial)なデストラクタと、constexpr
コンストラクタを持たせてください。 -
If all else fails, you can create an object dynamically and never delete
it by using a function-local static pointer or reference (e.g.,
static const auto& impl = *new T(args...);
). 上記のいずれの方法も採用できない場合には、関数ローカルの静的な生のポインタ変数か参照変数を用意し、オブジェクトを動的に作成して参照し、それをdeleteしないままにします。 (例:static const auto& impl = *new T(args...);
)
thread_local 変数
thread_local
variables that aren't declared inside a function
must be initialized with a true compile-time constant,
and this must be enforced by using the
constinit
attribute.
thread_local
変数が、関数スコープの外で宣言されるときは、真のコンパイル時定数で初期化されなくてはなりません。これはconstinit
属性を使用して強制されていなくてはなりません。
Prefer
thread_local
over other ways of defining thread-local data.
また、スレッドごとにローカルなデータを定義したい場合は、他の方法よりも、thread_local
を優先的に採用します。
Variables can be declared with the
thread_local
specifier:
変数は、次のように、thread_local
指定子をつけて宣言することができます。
thread_local Foo foo = ...;
Such a variable is actually a collection of objects, so that when different
threads access it, they are actually accessing different objects.
このような変数は、実際にはオブジェクトのコレクションになり、アクセスするスレッドごとに異なるオブジェクトを参照することになります。
thread_local
variables are much like
static storage duration variables
in many respects.
thread_local
変数は、多くの点で静的記憶域期間の変数によく似ています。
For instance, they can be declared at namespace scope,
inside functions, or as static class members, but not as ordinary class
members.
たとえば、thread_local
変数は、名前空間スコープ変数、関数内ローカル変数、または静的クラスメンバ変数として宣言できますが、通常のクラスメンバ変数として宣言することはできません。
thread_local
variable instances are initialized much like
static variables, except that they must be initialized separately for each
thread, rather than once at program startup.
thread_local
変数のインスタンスは、「プログラムの起動時に一度だけ」ではなく「スレッドごとに個別に」初期化されるという点を除けば、ほとんど静的変数と同じように初期化されます。
This means that
thread_local
variables declared within a function are safe, but
other thread_local
variables are subject to the same
initialization-order issues as static variables (and more besides).
これは、関数内で宣言されるthread_local
変数は安全であり、それ以外の場所で宣言されるthread_local
変数は、静的変数と同様に、初期化順序やその他の問題の影響を受けることを意味します。
thread_local
variabls have a subtle destruction-order issue:
during thread shutdown, thread_local
variables will be destroyed
in the opposite order of their initialization (as is generally true in C++).
thread_local
変数には、破壊順序に関する微妙な問題もあります。thread_local
変数は、スレッドが終了するときに破壊されますが、それらの破壊順序は、 (C++が全般的にそうであるように、) それらの初期化順の逆順となります。
If code triggered by the destructor of any thread_local
variable
refers to any already-destroyed thread_local
on that thread, we will
get a particularly hard to diagnose use-after-free.
あるthread_local
変数のデストラクタから呼び出されるコードが、同スレッドで既に破壊済みの別のthread_local
変数を参照していると、特に診断の難しい use-after-free バグ(デストラクタ実行後やメモリ解放後の変数にアクセスしてしまうバグ)を引き起こしてしまうかもしれません。
-
Thread-local data is inherently safe from races (because only one thread
can ordinarily access it), which makes
thread_local
useful for concurrent programming. スレッドローカルなデータは、通常は1つのスレッドからしかアクセスできないため、本質的に競合(race)に対して安全です。 この性質により、thread_local
は、並行プログラミングにおいて非常に有用です。 -
thread_local
is the only standard-supported way of creating thread-local data. スレッドローカルなデータの作成方法として、thread_local
は標準でサポートされている唯一の方法です。
-
Accessing a
thread_local
variable may trigger execution of an unpredictable and uncontrollable amount of other code during thread-start or first use on a given thread.thread_local
変数を用いると、スレッド開始時や、各スレッドで初めてその変数を使用するときに、その見た目からは予測も制御もできないような量のコードが実行される可能性があります。 -
thread_local
variables are effectively global variables, and have all the drawbacks of global variables other than lack of thread-safety.thread_local
変数は事実上グローバル変数であり、スレッドセーフである点を除けば、グローバル変数と同様の欠点を持ちます。 -
The memory consumed by a
thread_local
variable scales with the number of running threads (in the worst case), which can be quite large in a program.thread_local
変数は、(最悪の場合、)実行中のスレッド数に比例した量のメモリを使用するため、プログラム内に占める消費メモリの量が非常に大きくなる可能性があります。 -
Data members cannot be
thread_local
unless they are alsostatic
. データメンバをthread_local
とするためには、同時にそれらをstatic
にしなければなりません。 -
We may suffer from use-after-free bugs if
thread_local
variables have complex destructors.thread_local
変数が複雑なデストラクタを持っている場合には、use-after-freeバグに悩まされるかもしれません。 In particular, the destructor of any such variable must not call any code (transitively) that refers to any potentially-destroyedthread_local
. 特に、このような変数のデストラクタでは、推移的な呼び出しも含めて、潜在的に破壊済みかもしれない他のthread_local
変数を参照するコードを呼び出してはなりません。 This property is hard to enforce. しかし、この特性を強制するのは困難です。 -
Approaches for avoiding use-after-free in global/static contexts do not work for
thread_local
s. グローバル変数や静的変数のuse-after-freeバグを回避するテクニックは、thread_local
変数に対しては適用できません。 Specifically, skipping destructors for globals and static variables is allowable because their lifetimes end at program shutdown. 具体的には、グローバル変数や静的変数においては、その生存期間がプログラムの終了時までであるため、デストラクタを呼ばずにそのままプログラムを終了させてしまい、 Thus, any "leak" is managed immediately by the OS cleaning up our memory and other resources. この際に発生する何らかのリークを、OSによるメモリとリソースのクリーンアップ処理によって直ちに処理してもらうことで、問題とならないようにする手法が採用できました。 By contrast, skipping destructors forthread_local
variables leads to resource leaks proportional to the total number of threads that terminate during the lifetime of the program. しかし、それらの変数とは対照的に、thread_local
変数においてデストラクタ呼び出しをスキップしてしまうと、プログラムの実行期間中に終了する延べのスレッド数に比例して、リソースリークを引き起こしてしまうことになります。
thread_local
variables at class or namespace scope must be
initialized with a true compile-time constant (i.e., they must have no
dynamic initialization).
クラススコープや名前空間スコープのthread_local
変数は、真のコンパイル時定数で初期化されなければなりません。(すなわち、動的初期化されてはなりません。)
To enforce this, thread_local
variables
at class or namespace scope must be annotated with
constinit
(or constexpr
, but that should be rare):
これを強制するため、そのようなスコープのthread_local
変数は
constinit
でマークされていなければなりません (もしくはconstexpr
でマークしてもかまいませんが、こちらをthread_local
で使うのはレアケースでしょう)。
constinit thread_local Foo foo = ...;
thread_local
variables inside a function have no initialization
concerns, but still risk use-after-free during thread exit.
thread_local
変数を関数内スコープで宣言する場合は、初期化時の懸念事項はなくなります。ただし、依然としてスレッド終了時のuse-after-freeバグに関する危険性は残ります。
Note that you can use
a function-scope thread_local
to simulate a class- or
namespace-scope thread_local
by defining a function or
static method that exposes it:
なお、次のように、関数や静的数内スコープのthread_local
を定義することによって、クラススコープや名前空間スコープのthread_local
変数の代替手段として用いることができます。テクニックとして覚えておくとよいでしょう。
Foo& MyThreadLocalFoo() { thread_local Foo result = ComplicatedInitialization(); return result; }
Note that thread_local
variables will be destroyed whenever a thread exits.
thread_local
変数が破壊されるのはスレッド終了時です。
If the destructor of any such variable refers to any other (potentially-destroyed)
thread_local
we will suffer from hard to diagnose use-after-free bugs.
もし、これらの変数のデストラクタで、他の (潜在的に破壊済みかもしれない) thread_local
変数を参照していると、診断が困難なuse-after-freeバグに悩まされるかもしれません。
Prefer trivial types, or types that provably run no user-provided code at destruction to
minimize the potential of accessing any other thread_local
.
このような潜在的に問題のあるアクセスをなるべく避けるために、thread_local
変数には、自明(trivial)な型や、ユーザー定義デストラクタを持たない型を用いる方が望ましいでしょう。
thread_local
should be preferred over other mechanisms for
defining thread-local data.
また、スレッドローカルなデータを定義するときは、他の仕組みよりも、thread_local
を優先的に採用しましょう。
クラス
Classes are the fundamental unit of code in C++. クラスはC++におけるコードの基本単位です。 Naturally, we use them extensively. もちろん、私たちは、すでにクラスを広く使っています。 This section lists the main dos and don'ts you should follow when writing a class. このセクションでは、クラスを書くときに、すべきこと、すべきでないことについて説明します。
コンストラクタで行うこと
Avoid virtual method calls in constructors, and avoid initialization that can fail if you can't signal an error. コンストラクタで仮想メンバ関数を呼んではいけません。 また、エラーを伝える何らかの手段がない場合、失敗する可能性のある初期化処理を行ってはいけません。
It is possible to perform arbitrary initialization in the body of the constructor. コンストラクタでは任意の初期化処理を行うことができます。
- No need to worry about whether the class has been initialized or not. クラスが初期化済みであるか否かについて、心配しなくてよくなります。
-
Objects that are fully initialized by constructor call can
be
const
and may also be easier to use with standard containers or algorithms. コンストラクタによってオブジェクトが完全に初期化されるのであれば、そのオブジェクトをconst
にすることができます。また、標準コンテナや標準アルゴリズムで扱うことも容易になります。
- If the work calls virtual functions, these calls will not get dispatched to the subclass implementations. コンストラクタから仮想関数を呼び出したとしても、派生クラスの実装が呼び出されることはありません。 Future modification to your class can quietly introduce this problem even if your class is not currently subclassed, causing much confusion. 今の時点でクラスに派生クラスがなかったとしても、将来的にクラスが変更されたときに静かにこの問題が入り込み、混乱を招くことになるかもしれません。
- There is no easy way for constructors to signal errors, short of crashing the program (not always appropriate) or using exceptions (which are forbidden). コンストラクタには、エラーを伝える簡単な方法がありません。せいぜい、プログラムをクラッシュさせる(常にそれが適切とは限りません)か、例外を使う(ルール上は禁止です)方法くらいです。
-
If the work fails, we now have an object whose initialization
code failed, so it may be an unusual state requiring a
bool IsValid()
state checking mechanism (or similar) which is easy to forget to call. コンストラクタが処理に失敗したときは、正しく初期化されていない中途半端なオブジェクトができてしまうため、bool IsValid()
のような、状態をチェックする追加の仕組みを必要とするかもしれません。しかし、そのような仕組みを用意してすら、それを呼び出すこと自体も簡単に忘れられてしまいます。 - You cannot take the address of a constructor, so whatever work is done in the constructor cannot easily be handed off to, for example, another thread. コンストラクタのアドレスを得ることはできません。このため、コンストラクタの処理内容を他のスレッドなどに渡したりするようなことは、簡単にはできません。
Constructors should never call virtual functions.
コンストラクタでは仮想関数を呼び出してはいけません。
If appropriate
for your code ,
terminating the program may be an appropriate error handling
response.
コンストラクタで発生するエラーの処理の方法としては、適切な場合には、そのままプログラムを強制終了するのも1つの手段でしょう。
Otherwise, consider a factory function
or Init()
method as described in
TotW #42.
さもなくば、TotW #42で説明されているような、ファクトリ関数やInit()
メソッドの導入を検討しましょう。
Avoid Init()
methods on objects With
no other states that affect which public methods may be called
(semi-constructed objects of this form are particularly hard to work
with correctly).
どのパブリックメソッドを呼びだせるかに影響するような他の状態を持たないオブジェクトではInit()
メソッドは避けてください(この形の中途半端に構築されたオブジェクトを正しく扱うのは非常に困難です)。
暗黙的型変換
Do not define implicit conversions.
暗黙的型変換を定義してはいけません。
Use the explicit
keyword for conversion operators and single-argument
constructors.
型変換演算子や、引数1つのコンストラクタには、explicit
キーワードを使用してください。
Implicit conversions allow an
object of one type (called the source type) to
be used where a different type (called the destination
type) is expected, such as when passing an
int
argument to a function that takes a
double
parameter.
暗黙的型変換によって、ある型(変換元の型)のオブジェクトを、異なる型(変換先の型)が期待される箇所で用いることができるようになります。たとえば、double
型の引数をとる関数にint
型の値を渡すといった例が考えられます。
In addition to the implicit conversions defined by the language,
users can define their own, by adding appropriate members to the
class definition of the source or destination type.
言語で元々定義されている暗黙的型変換に加えて、変換元の型か変換先の型のクラス定義に適切なメンバを加えることで、独自の暗黙的型変換を定義することができます。
An implicit
conversion in the source type is defined by a type conversion operator
named after the destination type (e.g., operator
bool()
).
変換元の型で暗黙的型変換を定義するには、変換先の型の名前をもつ型変換演算子を定義します(例:operator bool()
)。
An implicit conversion in the destination
type is defined by a constructor that can take the source type as
its only argument (or only argument with no default value).
変換先の型で暗黙的型変換を定義するには、変換元の型の引数を1つだけとる(またはデフォルト値のない引数を1つだけとる)コンストラクタを定義します。
The explicit
keyword can be applied to a constructor
or a conversion operator, to ensure that it can only be
used when the destination type is explicit at the point of use,
e.g., with a cast.
explicit
キーワードは、コンストラクタや型変換演算子に適用することができ、キャストなどによって変換先の型が明示された場合にのみ変換が行われるように限定することができます。
This applies not only to implicit conversions, but to
list initialization syntax:
これは、暗黙的型変換を防ぐだけではなく、以下に示すようなリスト初期化構文にも適用されます。
class Foo { explicit Foo(int x, double y); ... }; void Func(Foo f);
Func({42, 3.14}); // ErrorThis kind of code isn't technically an implicit conversion, but the language treats it as one as far as
explicit
is concerned.
これに類するコードは、技術的には暗黙的型変換ではありませんが、言語系からは暗黙的変換の一種のように扱われ、explicit
の影響を受けることになります。
- Implicit conversions can make a type more usable and expressive by eliminating the need to explicitly name a type when it's obvious. 要求される型が明らかな箇所においては、暗黙的型変換によって型名の明示が不要になるため、型の利便性や表現性が高まります。
-
Implicit conversions can be a simpler alternative to
overloading, such as when a single
function with a
string_view
parameter takes the place of separate overloads forstd::string
andconst char*
. 暗黙的型変換を関数オーバーロードの代替手段として用いると、APIをよりシンプルにできます。 たとえば、引数にstd::string
を受け取る関数とconst char*
を受け取る関数とを別々に定義するかわりに、std::string_view
を引数とした関数を1つだけ用意して済ませることができます。 - List initialization syntax is a concise and expressive way of initializing objects. リスト初期化の構文は、オブジェクトの初期化方法として簡潔で表現的です。
- Implicit conversions can hide type-mismatch bugs, where the destination type does not match the user's expectation, or the user is unaware that any conversion will take place. 暗黙的型変換は、型の不一致に起因するバグを隠蔽してしまうことがあります。 たとえば、変数が期待と異なる型に変換されたり、そもそもその場で他の型への変換が行われていること自体に気づけなかったりするかもしれません。
- Implicit conversions can make code harder to read, particularly in the presence of overloading, by making it less obvious what code is actually getting called. 暗黙的型変換は、コードの可読性を下げることがあります。特に、関数がオーバーロードされている場合に、実際にどの関数が呼び出されるのかわかりにくくなってしまうことがあります。
- Constructors that take a single argument may accidentally be usable as implicit type conversions, even if they are not intended to do so. 引数1つのコンストラクタによって、意図せず型変換の機能を提供してしまうことがあります。
-
When a single-argument constructor is not marked
explicit
, there's no reliable way to tell whether it's intended to define an implicit conversion, or the author simply forgot to mark it. 引数1つのコンストラクタがexplicit
でマークされていないとき、そのコンストラクタが、暗黙的な型変換を意図したものなのか、あるいは単にexplicit
を付け忘れただけなのかを判断する手段がありません。 - Implicit conversions can lead to call-site ambiguities, especially when there are bidirectional implicit conversions. 暗黙的型変換によって、特に、ある2つの型が双方向に暗黙的型変換可能なとき、呼び出し元のコードが曖昧になることがあります。 This can be caused either by having two types that both provide an implicit conversion, or by a single type that has both an implicit constructor and an implicit type conversion operator. これは、それらの2つの型の双方において暗黙的型変換の機能が提供されているときや、片方の型において暗黙的型変換を行うコンストラクタと暗黙的型変換演算子の両方が実装されているときに起こります。
- List initialization can suffer from the same problems if the destination type is implicit, particularly if the list has only a single element. リスト初期化においても、変換先の型が暗黙的であるとき、特にリストの要素が1つだけであるときに、同様の問題に直面します。
Type conversion operators, and constructors that are
callable with a single argument, must be marked
explicit
in the class definition.
型変換演算子、および、引数1つで呼び出せるコンストラクタは、クラス定義においてexplicit
でマークされていなくてはなりません。
As an
exception, copy and move constructors should not be
explicit
, since they do not perform type
conversion.
例外として、コピーコンストラクタとムーブコンストラクタについては、explicit
でマークしてはいけません (これらのコンストラクタは型変換は行いません)。
Implicit conversions can sometimes be necessary and appropriate for
types that are designed to be interchangeable, for example when objects
of two types are just different representations of the same underlying
value.
場合によっては、暗黙的型変換が必要かつ適切であることもあります。たとえば、ある2つの型が、同じ意味の値に対して表現方法のみが異なるようなときは、型を相互に交換可能であるように設計してもよいでしょう。 ((訳注: std::chrono::duration
などでしょうか))
In that case, contact
your project leads to request
a waiver of this rule.
この場合は、プロジェクトリーダーと相談し、このルールの適用外としてください。
Constructors that cannot be called with a single argument
may omit explicit
.
引数1つで呼び出せないコンストラクタについては、explicit
を省略してかまいません。
Constructors that
take a single std::initializer_list
parameter should
also omit explicit
, in order to support copy-initialization
(e.g., MyType m = {1, 2};
).
また、引数にstd::initializer_list
を1つだけとるコンストラクタについても、コピーによる初期化(MyType m = {1, 2};
の形)をサポートするためにexplicit
を省略してください。
コピー可能な型・ムーブ可能な型
A class's public API must make clear whether the class is copyable, move-only, or neither copyable nor movable. クラスを定義するときは、その公開APIにおいて、その型のオブジェクトがコピー可能なのか、ムーブのみ可能なのか、あるいは、いずれも不可能なのか明確にわかるようにしておかなければなりません。 Support copying and/or moving if these operations are clear and meaningful for your type. オブジェクトのコピー操作やムーブ操作について明確かつ意味が通るのであれば、型でそれらの操作を提供してください。
A movable type is one that can be initialized and assigned from temporaries. 「型がムーブ可能である」とは、一時オブジェクトから新たなオブジェクトを初期化可能および代入可能であることを言います。
A copyable type is one that can be initialized or assigned from
any other object of the same type (so is also movable by definition), with the
stipulation that the value of the source does not change.
「型がコピー可能である」とは、同じ型の別のオブジェクトから、そのオブジェクトの値を変化させることなく、新たなオブジェクトを初期化可能および代入可能であることを言います。(その定義から、コピー可能な型は、同時にムーブ可能でもあります。)
std::unique_ptr<int>
is an example of a movable but not
copyable type (since the value of the source
std::unique_ptr<int>
must be modified during assignment to
the destination).
たとえば、std::unique_ptr<int>
は、ムーブ可能であるがコピーは不可な型の一例です。(別のstd::unique_ptr<int>
変数への代入操作を行うときには、代入操作に使われる元の値が変更される必要があります。)
int
and std::string
are examples of
movable types that are also copyable.
int
やstd::string
は、ムーブもコピーも可能な型の例です。
(For int
, the move and copy
operations are the same; for std::string
, there exists a move operation
that is less expensive than a copy.)
(int
においてはムーブ操作とコピー操作が同一となり、
std::string
においては、コピー操作よりも低コストなムーブ操作が提供されています。)
For user-defined types, the copy behavior is defined by the copy
constructor and the copy-assignment operator.
ユーザー定義の型において、コピーの挙動はコピーコンストラクタとコピー代入演算子によって定義されます。
Move behavior is defined by the
move constructor and the move-assignment operator, if they exist, or by the
copy constructor and the copy-assignment operator otherwise.
ムーブの挙動は、ムーブコンストラクタとムーブ代入演算子が定義されていれば、それらによって定義され、定義されていなければコピー操作と同一になります。
The copy/move constructors can be implicitly invoked by the compiler in some situations, e.g., when passing objects by value. コピーコンストラクタやムーブコンストラクタは、オブジェクトを値渡しするときなどに、コンパイラによって暗黙的に呼び出されます。
Objects of copyable and movable types can be passed and returned by value, which makes APIs simpler, safer, and more general. コピー可能な型・ムーブ可能な型のオブジェクトは値渡し・値戻しすることができます。これによって、よりシンプルで、より安全で、より一般的な形のAPIを定義することができるようになります。 Unlike when passing objects by pointer or reference, there's no risk of confusion over ownership, lifetime, mutability, and similar issues, and no need to specify them in the contract. 値渡しは、ポインタ渡し・参照渡しと異なり、オブジェクトの所有権や寿命、変更可能性、その他のそれに類する危険がなく、APIにおいてそれらに関する前提条件を定める必要がなくなります。 It also prevents non-local interactions between the client and the implementation, which makes them easier to understand, maintain, and optimize by the compiler. また、呼び出し元と実装をつないでしまうような非局所的な相互作用も自動的に防がれるため、オブジェクトを理解しやすくメンテナンスしやすい形で実装することができ、同時に、コンパイラによる最適化も掛かりやすくなります。 Further, such objects can be used with generic APIs that require pass-by-value, such as most containers, and they allow for additional flexibility in e.g., type composition. さらに、このようなオブジェクトは、多くのコンテナ型のように値渡しを要求する一般的なAPIにも用いることができますし、あるいは、型の抱合(composition)などを行う際にも、より柔軟に対応することができるでしょう。
Copy/move constructors and assignment operators are usually
easier to define correctly than alternatives
like Clone()
, CopyFrom()
or Swap()
,
because they can be generated by the compiler, either implicitly or
with = default
.
コピーコンストラクタ、ムーブコンストラクタ、また関連する代入演算子は、暗黙的に、あるいは明示的に= default
と記述することで、その実装をコンパイラに生成させることができます。このため、Clone()
, CopyFrom()
, Swap()
のような代替手段を実装するのに比べて、比較的、正しい実装を得ることが容易です。
They are concise, and ensure
that all data members are copied.
自動生成されるコンストラクタや代入演算子は簡潔で、しかも、すべてのメンバがコピーされることが保証されます。
Copy and move
constructors are also generally more efficient, because they don't
require heap allocation or separate initialization and assignment
steps, and they're eligible for optimizations such as
copy elision.
また、一般に、これらの代替手段よりも、コピーコンストラクタやムーブコンストラクタを用いる方が効率的にもなりやすいです。これらのコンストラクタを用いる場合は、ヒープメモリの確保が不要になり、初期化と割り当てをまとめて行えて、かつ、コピーの省略のような最適化に対しても適格となるためです。
Move operations allow the implicit and efficient transfer of resources out of rvalue objects. ムーブ操作によって、右辺値オブジェクトから暗黙的かつ効率的にリソースを取り出すことが可能になります。 This allows a plainer coding style in some cases. また、場合によっては、より簡潔なコーディングスタイルを採用できるようにもなるでしょう。
Some types do not need to be copyable, and providing copy
operations for such types can be confusing, nonsensical, or outright
incorrect.
型によっては、コピー操作が不要であったり、そもそもコピー操作を提供すること自体が、概念上おかしい場合があります。
Types representing singleton objects (Registerer
),
objects tied to a specific scope (Cleanup
), or closely coupled to
object identity (Mutex
) cannot be copied meaningfully.
たとえば、(Registerer
のような)シングルトンオブジェクトや、(Cleanup
のような)そのスコープに紐付くオブジェクト、あるいは(Mutex
のような)その識別子自体と紐付けて使われるオブジェクトは、意味のある形でコピーを定義できません。
Copy operations for base class types that are to be used polymorphically are hazardous, because use of them can lead to object slicing. ポリモーフィズムを使用しているような型においては、基底クラスに対するコピー操作は、オブジェクトのスライシングを引き起こす可能性があるため、非常に危険です。 Defaulted or carelessly-implemented copy operations can be incorrect, and the resulting bugs can be confusing and difficult to diagnose. コピー操作をデフォルトのままにしていたり、深く考えずにコピー操作を定義してしまうと、それらの実装は正しくない実装になる可能性があり、その結果として診断の難しい困惑的なバグを招くかもしれません。
Copy constructors are invoked implicitly, which makes the invocation easy to miss. また、コピーコンストラクタは暗黙的に呼び出されるため、その呼び出しそのものも見落としやすいです。 This may cause confusion for programmers used to languages where pass-by-reference is conventional or mandatory. これは、参照渡しが慣習的(あるいは必須)であるような他のプログラミング言語に慣れ親しんでいるプログラマーにとっては、特に困惑的なポイントかもしれません。 It may also encourage excessive copying, which can cause performance problems. また、これによって、オブジェクトの過度なコピーが助長され、パフォーマンス上の問題となって表れるかもしれません。
Every class's public interface must make clear which copy and move
operations the class supports.
すべてのクラスの公開インターフェースにおいて、その型がサポートするコピー・ムーブ操作について明確にしてください。
This should usually take the form of explicitly
declaring and/or deleting the appropriate operations in the public
section of the declaration.
これは、通常は、クラス定義のpublic
セクションにおいて、対応する操作を明示的に宣言あるいは削除することによって行います。
Specifically, a copyable class should explicitly declare the copy operations, a move-only class should explicitly declare the move operations, and a non-copyable/movable class should explicitly delete the copy operations. すなわち、コピー可能なクラスではコピー操作を明示的に宣言し、ムーブのみ可能なクラスではムーブ操作を明示的に宣言し、コピーもムーブもできないクラスでは、コピー操作を明示的に削除しましょう。 A copyable class may also declare move operations in order to support efficient moves. コピー可能クラスにおいては、より効率的なムーブを提供するために、追加のムーブ操作を宣言をしてもかまいません。 Explicitly declaring or deleting all four copy/move operations is permitted, but not required. 4つのコピー・ムーブ操作すべてを明示的に宣言または削除してもかまいませんが、必須ではありません。 If you provide a copy or move assignment operator, you must also provide the corresponding constructor. ただし、コピー代入演算子やムーブ代入演算子を提供する場合は、必ずその操作に対応するコンストラクタも提供するようにしてください。
class Copyable { public: Copyable(const Copyable& other) = default; Copyable& operator=(const Copyable& other) = default; // The implicit move operations are suppressed by the declarations above. // You may explicitly declare move operations to support efficient moves. }; class MoveOnly { public: MoveOnly(MoveOnly&& other) = default; MoveOnly& operator=(MoveOnly&& other) = default; // The copy operations are implicitly deleted, but you can // spell that out explicitly if you want: MoveOnly(const MoveOnly&) = delete; MoveOnly& operator=(const MoveOnly&) = delete; }; class NotCopyableOrMovable { public: // Not copyable or movable NotCopyableOrMovable(const NotCopyableOrMovable&) = delete; NotCopyableOrMovable& operator=(const NotCopyableOrMovable&) = delete; // The move operations are implicitly disabled, but you can // spell that out explicitly if you want: NotCopyableOrMovable(NotCopyableOrMovable&&) = delete; NotCopyableOrMovable& operator=(NotCopyableOrMovable&&) = delete; };
class Copyable { public: Copyable(const Copyable& other) = default; Copyable& operator=(const Copyable& other) = default; // 暗黙的ムーブ操作は、上記の宣言によって抑制されます。 // 効率的なムーブをサポートするために、明示的にムーブ操作を宣言してもかまいません。 }; class MoveOnly { public: MoveOnly(MoveOnly&& other) = default; MoveOnly& operator=(MoveOnly&& other) = default; // コピー操作は暗黙的に削除されます。 // 次のように、明示的に宣言してもかまいません。 MoveOnly(const MoveOnly&) = delete; MoveOnly& operator=(const MoveOnly&) = delete; }; class NotCopyableOrMovable { public: // コピーもムーブも不可 NotCopyableOrMovable(const NotCopyableOrMovable&) = delete; NotCopyableOrMovable& operator=(const NotCopyableOrMovable&) = delete; // この例では、ムーブ操作は暗黙的に無効になります。 // 次のように、明示的に宣言してもかまいません。 NotCopyableOrMovable(NotCopyableOrMovable&&) = delete; NotCopyableOrMovable& operator=(NotCopyableOrMovable&&) = delete; };
These declarations/deletions can be omitted only if they are obvious: これらの宣言や削除宣言は、型がどの操作をサポートするか明確であるときに限り、省略してもかまいません。
-
If the class has no
private
section, like a struct or an interface-only base class, then the copyability/movability can be determined by the copyability/movability of any public data members. 構造体やインターフェース基底クラスのような、private
セクションを持たないクラスのコピー・ムーブ可能性は、そのクラスの公開データメンバのコピー・ムーブ可能性によって決定できます。 - If a base class clearly isn't copyable or movable, derived classes naturally won't be either. 基底クラスが明らかにコピー不可(あるいはムーブ不可)なとき、派生クラスも当然にコピー不可(あるいはムーブ不可)となります。 An interface-only base class that leaves these operations implicit is not sufficient to make concrete subclasses clear. 逆に、インターフェースを定義する基底クラスにおいて、それらの操作が暗黙のままにされているときは、その派生クラスにおいて可能な操作の種類が明確であるとは言えません。
- Note that if you explicitly declare or delete either the constructor or assignment operation for copy, the other copy operation is not obvious and must be declared or deleted. なお、コピーコンストラクタやコピー代入演算子のいずれかだけを明示的に宣言または削除した場合に、もう一方のコピー操作まで自動的に自明となるわけではありません。常に、コンストラクタと代入演算子の両方の操作について、宣言または削除するようにしてください。 Likewise for move operations. ムーブ操作についても同様です。
A type should not be copyable/movable if the meaning of copying/moving is unclear to a casual user, or if it incurs unexpected costs. カジュアルなユーザーから見て、ある型のコピー・ムーブ操作の意味が不明瞭であるとき、あるいは、それらの操作によって予期しないコストが発生するときは、その型をコピー可能あるいはムーブ可能にしないでください。 Move operations for copyable types are strictly a performance optimization and are a potential source of bugs and complexity, so avoid defining them unless they are significantly more efficient than the corresponding copy operations. コピー可能な型における追加のムーブ操作は、厳密に考えるとパフォーマンス上の最適化にあたり、潜在的にバグや複雑さの要因となります。よって、このような型の追加のムーブ操作は、そのコピー操作よりもはるかに効率的に実現できるような場合に限って定義するものとし、それ以外の場合には実装しないでください。 If your type provides copy operations, it is recommended that you design your class so that the default implementation of those operations is correct. 新しい型でコピー操作を提供する場合は、できるだけ、コンパイラが生成するデフォルト実装がそのまま正しい実装となるように型を設計してください。 Remember to review the correctness of any defaulted operations as you would any other code. また、コピー・ムーブ操作について、コンパイラ生成のデフォルト実装に任せる場合であっても、それらのデフォルト実装によって正しくコピーあるいはムーブの操作が実現できているのかどうか、他のコードと同じようにレビューするようにしてください。
To eliminate the risk of slicing, prefer to make base classes abstract,
by making their constructors protected, by declaring their destructors protected,
or by giving them one or more pure virtual member functions.
オブジェクトスライシングのリスクを排除するため、継承における基底クラスは抽象クラスとする方が望ましいです。クラスを抽象クラスにするためには、コンストラクタをprotected
にするか、デストラクタをprotected
にするか、あるいは、1つ以上の純粋仮想メンバ関数を宣言します。
Prefer to avoid
deriving from concrete classes.
また、具象クラスからさらなる派生クラスを作ることは避ける方が望ましいです。
構造体(struct
)か、クラス(class
)か
Use a struct
only for passive objects that
carry data; everything else is a class
.
struct
はデータを運ぶための受動的なオブジェクトにのみ使用します。それ以外のすべての用途でclass
を使用します。
The struct
and class
keywords behave almost identically in C++.
C++において、struct
とclass
はほとんど同じ振る舞いをします。
We add our own
semantic meanings to each keyword, so you should use the
appropriate keyword for the data-type you're
defining.
私たちは、それぞれのキーワードに独自の意味づけをしており、定義するデータの種類に応じて適切なキーワードを使い分けています。
structs
should be used for passive objects that carry
data, and may have associated constants.
struct
はデータを運ぶための受動的なオブジェクトに使用します。関連する定数を含んでもかまいません。
All fields must be public.
すべてのフィールドはpublic
でなければなりません。
The
struct must not have invariants that imply relationships between
different fields, since direct user access to those fields may
break those invariants.
異なるフィールド間にまたがる暗黙的な不変条件を持ってはいけません。
これらのフィールドは、ユーザーによって直接アクセスされるため、そのような不変条件はそもそも維持することができません。
Constructors, destructors, and helper methods may
be present; however, these methods must not require or enforce any
invariants.
コンストラクタやデストラクタ、その他のヘルパーメソッドを定義してもかまいません。
しかし、それらのメソッドにおいても、何らかの不変条件を要求したり強制したりしてはいけません。
If more functionality or invariants are required, or struct has wide visibility and expected to
evolve, then a class
is more appropriate.
それ以上の機能性や不変条件を必要とする場合、あるいは、その構造体が広い範囲から可視であり、今後の拡張が予見される場合には、class
を使う方が適切です。
If in doubt, make
it a class
.
迷ったときはclass
にしてください。
For consistency with STL, you can use
struct
instead of class
for
stateless types, such as traits,
template metafunctions,
and some functors.
型トレイトやテンプレートメタ関数、関数オブジェクトなどに代表されるような、状態を何も持たない型については、STLとの一貫性を保つ目的でclass
のかわりにstruct
を使ってもかまいません。
Note that member variables in structs and classes have
different naming rules.
なお、struct
とclass
では、メンバ変数の命名規則が異なりますので注意してください。
構造体(struct)か、ペア(pair)・タプル(tupple)か
Prefer to use a struct
instead of a pair or a
tuple whenever the elements can have meaningful names.
その要素に意味のある名前を付けられるならば、pair
やtuple
で済ませることなく、常にstruct
を定義して使います。
While using pairs and tuples can avoid the need to define a custom type,
potentially saving work when writing code, a meaningful field
name will almost always be much clearer when reading code than
.first
, .second
, or std::get<X>
.
ペアやタプルを用いると、型を独自に定義する手間を省けるため、コードを書くときに、いくらか楽をすることができるかもしれません。しかし、コードを読むときのことを考えれば、.first
や.second
、std::get<0>()
などよりも、意味のあるフィールド名を用いる方が、ほとんど常に、はるかにわかりやすいはずです。
While C++14's introduction of std::get<Type>
to access a
tuple element by type rather than index (when the type is unique) can
sometimes partially mitigate this, a field name is usually substantially
clearer and more informative than a type.
タプルの中で型が一意なときは、C++14からのstd::get<Type>()
を用いることで、インデックスではなく型名で要素にアクセスできるため、この状況はいくらか改善するかもしれません。しかし、いずれにせよ、通常は型名よりもフィールド名の方が明確であり、含まれる情報量も多いでしょう。
Pairs and tuples may be appropriate in generic code where there are not specific meanings for the elements of the pair or tuple. ペアやタプルは、その各要素に特定の意味を定められないような、汎用的なコードにおいては適切な場合もあります。 Their use may also be required in order to interoperate with existing code or APIs. あるいは、既存のコードやAPIとの相互運用のためにペアやタプルを必要とすることもあるでしょう。
継承
Composition is often more appropriate than inheritance.
継承よりも抱合(composition)を用いる方がより適切な場合も多いです。
When using inheritance, make it public
.
継承を用いるときは、必ずpublic
にします。
When a sub-class inherits from a base class, it includes the definitions of all the data and operations that the base class defines. あるクラスが何らかの基底クラスを継承するとき、その派生クラスには基底クラスが定義するすべてのデータと操作が含まれます。 "Interface inheritance" is inheritance from a pure abstract base class (one with no state or defined methods); all other inheritance is "implementation inheritance". 「インターフェースの継承」は、純粋抽象基底クラス(状態やメソッドの定義を一切持たないクラス)からの継承のことを言い、それ以外の継承はすべて「実装の継承」です。
Implementation inheritance reduces code size by re-using the base class code as it specializes an existing type. 実装の継承は、既存の型を特殊化する際に基底クラスのコードを再利用するため、コードのサイズが小さくなります。 Because inheritance is a compile-time declaration, you and the compiler can understand the operation and detect errors. 継承はコンパイル時の宣言であるため、プログラマーとコンパイラとの両者によってその操作が理解され、エラーがあれば検出することができます。 Interface inheritance can be used to programmatically enforce that a class expose a particular API. インターフェースの継承を用いると、派生クラスに対して所定のAPIを公開することをプログラム的に強制できます。 Again, the compiler can detect errors, in this case, when a class does not define a necessary method of the API. APIに要求されるメソッドが派生クラスにおいて定義されていないとき、やはり、コンパイラはそれをエラーとして検出することができます。
For implementation inheritance, because the code
implementing a sub-class is spread between the base and
the sub-class, it can be more difficult to understand an
implementation.
実装の継承を行うと、派生クラスの実装コードが物理的に基底クラスと派生クラスとの間に分散してしまうため、実装を理解するのがより難しくなるかもしれません。
The sub-class cannot override functions
that are not virtual, so the sub-class cannot change
implementation.
また、派生クラスでは、基底クラスでvirtual
宣言されていない関数をオーバーライドできないため、派生クラスでそれらの実装を変えることはできません。
Multiple inheritance is especially problematic, because it often imposes a higher performance overhead (in fact, the performance drop from single inheritance to multiple inheritance can often be greater than the performance drop from ordinary to virtual dispatch), and because it risks leading to "diamond" inheritance patterns, which are prone to ambiguity, confusion, and outright bugs. 多重継承は特に問題を引き起こしやすいです。 多重継承を行うとパフォーマンス上のオーバーヘッドが大きくなります (実際、単一継承から多重継承に変更する際に生ずるパフォーマンス低下は、通常の関数呼び出しを仮想呼び出しに変更する際のものと比べても大きくなりがちです)。 また、曖昧で困惑的であからさまなバグを引き起こしがちな、「ダイヤモンド継承」パターンに繋がってしまうリスクもあります。
All inheritance should be public
.
すべての継承はpublic
で行います。
If you
want to do private inheritance, you should be including
an instance of the base class as a member instead.
private
な継承を行いたいときは、継承のかわりに、基底クラスのインスタンスをメンバとして持つようにしてください。
You may use
final
on classes when you don't intend to support using
them as base classes.
基底クラスとして使用されることを想定しないクラスにはfinal
をつけてもかまいません。
Do not overuse implementation inheritance.
実装の継承を濫用しないでください。
Composition
is often more appropriate.
多くの場合において抱合(composition)の方がより適切です。
Try to restrict use of
inheritance to the "is-a" case: Bar
subclasses Foo
if it can reasonably be said
that Bar
"is a kind of"
Foo
.
継承は「is-a」関係が成立する場合に限って使用するようにしてください。
たとえばFoo
を継承してBar
を作ってよいのは、理屈の上で「Bar
はFoo
の一種」と言えるときだけです。
Limit the use of protected
to those
member functions that might need to be accessed from
subclasses.
protected
セクションを用いるのは、派生クラスからのアクセスが必要なメンバ関数だけにとどめてください。
Note that
data
members should be private
.
なお、データメンバはすべてprivate
としてください。
Explicitly annotate overrides of virtual functions
or virtual destructors with exactly one of an override
or (less frequently) final
specifier.
仮想関数や仮想デストラクタをオーバーライドするときは、override
指定子か(それほど頻繁ではありませんが)final
指定子のいずれか1つをつけて、オーバーライドしていることを明示してください。
Do not use virtual
when declaring an override.
オーバーライドを明示する目的でvirtual
を使ってはいけません。
Rationale: A function or destructor marked
override
or final
that is
not an override of a base class virtual function will
not compile, and this helps catch common errors.
この理由としては、仮想関数や仮想デストラクタをオーバーライドするとき、その意図をoverride
かfinal
で表しておくことで、それらの宣言が基底クラスの関数をオーバーライドしなかったときにコンパイルエラーとなり、間違いに気づくことができるようになるからです。
The
specifiers serve as documentation; if no specifier is
present, the reader has to check all ancestors of the
class in question to determine if the function or
destructor is virtual or not.
そして、これらの指定子はドキュメントの役割も果たします。これらの指定子が書かれていないものがあると、コードの読者は、それぞれの関数やデストラクタが仮想なのか否かを把握するために、すべての継承元をたどって調べなければならなくなってしまいます。
Multiple inheritance is permitted, but multiple implementation inheritance is strongly discouraged. 多重継承は禁止しませんが、実装の多重継承は、強く非推奨とします。
演算子のオーバーロード
Overload operators judiciously. 演算子のオーバーロードは慎重に。 Do not use user-defined literals. ユーザー定義リテラルは使ってはいけません。
C++ permits user code to
declare
overloaded versions of the built-in operators
using the
operator
keyword, so long as one of the parameters
is a user-defined type.
C++では、operator
キーワードを使うことで、ユーザー定義の型を引数とした組み込み演算子のオーバーロードを宣言することができます。
The operator
keyword also
permits user code to define new kinds of literals using
operator""
, and to define type-conversion functions
such as operator bool()
.
また、operator""
を用いることで新しいリテラルを定義することもできます。この他、operator bool()
のような、型変換を行う関数を定義することもできます。
Operator overloading can make code more concise and
intuitive by enabling user-defined types to behave the same
as built-in types.
演算子のオーバーロードを用いると、ユーザー定義型を組み込み型と同様に振る舞わせることができるようになり、コードがより簡潔で直感的になります。
Overloaded operators are the idiomatic names
for certain operations (e.g., ==
, <
,
=
, and <<
), and adhering to
those conventions can make user-defined types more readable
and enable them to interoperate with libraries that expect
those names.
オーバーロードされた演算子は、所定の操作に対する慣例的な見た目(==
, <
, =
, <<
など)をしており、これらの慣例に従うことによって、ユーザー定義の型に対する可読性を向上させ、また、このような演算子の存在を前提とするライブラリとの相互運用性も高めることができます。
User-defined literals are a very concise notation for creating objects of user-defined types. また、ユーザー定義リテラルは、ユーザー定義のオブジェクトを生成するための非常に簡潔な表記方法を提供します。
- Providing a correct, consistent, and unsurprising set of operator overloads requires some care, and failure to do so can lead to confusion and bugs. 正しい、一貫性のある、普通の、演算子のセットを提供するためには、細やかな注意を必要とし、それに失敗すると困惑とバグに繋がります。
- Overuse of operators can lead to obfuscated code, particularly if the overloaded operator's semantics don't follow convention. 演算子を濫用した場合、特に、演算子をその慣例に従わない意味で用いた場合、コードがわかりにくくなることがあります。
- The hazards of function overloading apply just as much to operator overloading, if not more so. 演算子のオーバーロードには、関数をオーバーロードする際と同等の(あるいはそれ以上の)危険性があります。
- Operator overloads can fool our intuition into thinking that expensive operations are cheap, built-in operations. オーバーロードされた演算子は、その見た目に反して高コストな処理を、(あたかも組み込み演算子が行うような)低コストの処理に見せかけてしまいます。
- Finding the call sites for overloaded operators may require a search tool that's aware of C++ syntax, rather than e.g., grep. オーバーロードされた演算子の呼び出し元を検索するためには、もはやgrepなどでは用をなさず、C++の文法を理解するツールを使用することが必要となります。
-
If you get the argument type of an overloaded operator
wrong, you may get a different overload rather than a
compiler error.
オーバーロードされた演算子の引数の型を間違えてしまったとき、コンパイルエラーにならずに、そのまま別のオーバーロード呼び出しに解決されてコンパイルが通ってしまうことがあります。
For example,
foo < bar
may do one thing, while&foo < &bar
does something totally different. たとえば、foo < bar
と&foo < &bar
とでは、まったく意味が異なります。 -
Certain operator overloads are inherently hazardous.
演算子によっては、それをオーバーロードすること自体に潜在的な危険性を含むものがあります。
Overloading unary
&
can cause the same code to have different meanings depending on whether the overload declaration is visible. たとえば、単項演算子&
のオーバーロードは、呼び出し元からその宣言が見えているか否かで意味が変わってしまいます。 Overloads of&&
,||
, and,
(comma) cannot match the evaluation-order semantics of the built-in operators.&&
、||
、や,
(カンマ演算子)をオーバーロードした場合、組み込み演算子で定められている式の評価順序と一致させることはできません。 - Operators are often defined outside the class, so there's a risk of different files introducing different definitions of the same operator. 演算子はしばしばクラスの外側で定義されますが、その場合に、同じ演算子に対して別のファイルにある異なる定義を引き込んでしまうリスクがあります。 If both definitions are linked into the same binary, this results in undefined behavior, which can manifest as subtle run-time bugs. 同じバイナリ内に、実装の異なる両方の演算子がリンクされてしまった場合、未定義動作を引き起こし、非常に微妙な実行時バグとなって現れるかもしれません。
-
User-defined literals (UDLs) allow the creation of new
syntactic forms that are unfamiliar even to experienced C++
programmers, such as
"Hello World"sv
as a shorthand forstd::string_view("Hello World")
. ユーザー定義リテラル(UDL)は、文法的に新しい形式を生み出します。たとえば"Hello World"sv
でstd::string_view("Hello World")
の略記になります。しかし、このような形式は十分に経験を積んだC++プログラマーにとっても馴染みの浅いものです。 Existing notations are clearer, though less terse. 従来からある表記方法は、簡潔でこそありませんが、わかりやすさの面で勝ります。 -
Because they can't be namespace-qualified, uses of UDLs also require
use of either using-directives (which we ban) or
using-declarations (which we ban in header files except
when the imported names are part of the interface exposed by the header
file in question).
UDLは名前空間修飾ができないため、UDLを使うためには、
using
ディレクティブ(は、禁止です)かusing
宣言(は、ヘッダーファイルでは一部例外を除いて禁止です)のいずれかを行う必要があります。 Given that header files would have to avoid UDL suffixes, we prefer to avoid having conventions for literals differ between header files and source files. これらのことから、事実上、ヘッダーファイルではUDL接尾辞が使えないことになります。かといって、ヘッダーファイルとソースファイルとの間でリテラルに関するルールが分かれてしまうような事態も望ましくありません。
Define overloaded operators only if their meaning is
obvious, unsurprising, and consistent with the corresponding
built-in operators.
演算子のオーバーロードは、コード読者から見て、その意味が明らかであり、動作を予測可能であり、かつ、組み込み演算子との一貫性が保てるときに限り定義してください。
For example, use |
as a
bitwise- or logical-or, not as a shell-style pipe.
たとえば、|
演算子は、ビット和か論理和の意味でのみ使い、シェルのパイプのような意味では使ってはいけません。
Define operators only on your own types.
演算子は、自分で定義した型に対するもののみを定義してください。
More precisely,
define them in the same headers, .cc
files, and namespaces
as the types they operate on.
より正確には、ある型に対する演算子は、その型の定義と同じヘッダーファイルか.cc
ファイルの中で、その型と同じ名前空間の中で定義してください。
That way, the operators are available
wherever the type is, minimizing the risk of multiple
definitions.
そのようにしておけば、型が利用可能なところでは常に演算子も利用可能となり、同一の演算子が多重定義されるリスクを最小限にとどめることができます。
If possible, avoid defining operators as templates,
because they must satisfy this rule for any possible template
arguments.
できれば、演算子をテンプレートとして定義するのは避けてください。
テンプレートとして宣言された演算子は、取り得る限りすべてのテンプレート引数において、これらのルールを満たしていなければなりません。
If you define an operator, also define
any related operators that make sense, and make sure they
are defined consistently.
ある演算子を定義する場合には、それに関連する演算子も正しく定義し、必ず一貫性を保つようにしてください。
For example, if you overload
<
, overload all the comparison operators,
and make sure <
and >
never
return true for the same arguments.
たとえば、<
をオーバーロードしたならば、その他のすべての比較演算子もオーバーロードしてください。
そして、ある引数の組に対する比較演算<
と>
の結果が両方とも真となるようなことがないようにしてください。
Prefer to define non-modifying binary operators as
non-member functions.
引数に対して変更を伴わない二項演算子は、非メンバ関数として定義される方が望ましいです。
If a binary operator is defined as a
class member, implicit conversions will apply to the
right-hand argument, but not the left-hand one.
二項演算子がクラスのメンバとして定義されていると、演算子の右辺については暗黙的型変換が適用されますが、左辺には適用されなくなってしまいます。
It will
confuse your users if a + b
compiles but
b + a
doesn't.
a + b
がコンパイルできてb + a
がコンパイルエラーになるような状況は、ユーザーの困惑を招きます。
For a type T
whose values can be compared for
equality, define a non-member operator==
and document when
two values of type T
are considered equal.
ある型T
がその値の等値性を比較できるとき、非メンバのoperator==
を定義した上で、どういう場合にそれらが等値と見なされるのかについてドキュメント化してください。
If there is a single obvious notion of when a value t1
of type T
is less than another such value t2
then
you may also define operator<=>
, which should be
consistent with operator==
.
また、型T
の値t1
と別の値t2
を大小比較するための、唯一で、かつ明らかな概念が存在するならば、operator<=>
を定義してもかまいません。
その際、operator==
との一貫性を保ってください。
Prefer not to overload the other comparison and ordering operators.
また、これ以外の比較演算子はオーバーロードしない方がよよいでしょう。
Don't go out of your way to avoid defining operator
overloads.
演算子のオーバーロードをわざわざ避けるようなことはしないでください。
For example, prefer to define ==
,
=
, and <<
, rather than
Equals()
, CopyFrom()
, and
PrintTo()
.
Equals()
やCopyFrom()
、PrintTo()
などを用意するよりも、==
、=
、<<
を定義する方が望ましいです。
Conversely, don't define
operator overloads just because other libraries expect
them.
反対に、他のライブラリの要件に合わせる目的のためだけに演算子をオーバーロードすることは避けてください。
For example, if your type doesn't have a natural
ordering, but you want to store it in a std::set
,
use a custom comparator rather than overloading
<
.
たとえば、値の大小比較関係を自然に定義できない型のオブジェクトをstd::set
に入れたいときであっても、この目的で比較演算子<
をオーバーロードしてはいけません。このような場合には、独自の比較子(comparator)を使うようにしてください。
Do not overload &&
, ||
,
,
(comma), or unary &
.
演算子&&
と||
と,
(カンマ演算子)、単項演算子&
はオーバーロードをしてはいけません。
Do not overload
operator""
, i.e., do not introduce user-defined
literals.
また、operator""
もオーバーロードしてはいけません。すなわち、ユーザー定義リテラルを導入してはなりません。
Do not use any such literals provided by others
(including the standard library).
そして、標準ライブラリも含めて、他者から提供されるいかなるリテラル演算子も使用してはなりません。
Type conversion operators are covered in the section on
implicit conversions.
型変換演算子については暗黙的型変換のセクションでカバーします。
The =
operator is covered in the section on
copy constructors.
代入演算子=
はコピーコンストラクタのセクションでカバーします。
Overloading
<<
for use with streams is covered in the
section on streams.
ストリーム用途での演算子<<
のオーバーロードについては、ストリームのセクションでカバーします。
See also the rules on
function overloading, which
apply to operator overloading as well.
また、これらを含む、すべての演算子のオーバーロードにおいて、関数のオーバーロードにあるルールも同様に適用されますので、あわせて参照してください。
アクセス制限
Make classes' data members private
, unless they are
constants.
クラスのデータメンバは、定数を除いて、すべてprivate
にします。
This simplifies reasoning about invariants, at the cost
of some easy boilerplate in the form of accessors (usually const
) if necessary.
このルールは、定型文的なgetter関数(通常はconst
)の実装を強制しますが、それを差し引いても、クラスの不変条件に関する議論をシンプルにできるメリットがあります。
For technical
reasons, we allow data members of a test fixture class defined in a .cc
file to
be protected
when using
Google
Test
.
Google Testを使う場合、技術的な理由により、テストフィクスチャクラスが.cc
ファイル内で定義される場合に限り、そのデータメンバをprotected
にしてもよいとします。
If a test fixture class is defined outside of the .cc
file it is used in, for example in a .h
file,
make data members private
.
それ以外の場所(たとえば.h
ファイル内)でテストフィクスチャクラスが定義される場合は、データメンバはprivate
にしなければなりません。
宣言の順序
Group similar declarations together, placing public
parts
earlier.
似ている宣言をグループにまとめます。public
部分を先頭に置きます。
A class definition should usually start with a
public:
section, followed by
protected:
, then private:
.
クラスの定義は、通常はpublic:
セクションから始め、protected:
、private:
と続けます。
Omit
sections that would be empty.
中身のないセクションは省略してください。
Within each section, prefer grouping similar kinds of declarations together, and prefer the following order: 各セクションにおいて、似た種類の宣言をまとめるようにし、以下の順に並べるようにします。
-
Types and type aliases (
typedef
,using
,enum
, nested structs and classes, andfriend
types) 型と型エイリアス(typedef
,using
,enum
, ネストされた構造体やクラス,friend
型) -
(Optionally, for structs only) non-
static
data members (構造体に限り、必要に応じて)非static
データメンバ - Static constants 静的定数
- Factory functions ファクトリ関数
- Constructors and assignment operators コンストラクタと代入演算子
- Destructor デストラクタ
-
All other functions (
static
and non-static
member functions, andfriend
functions) その他のすべての関数 (static
と非static
メンバ関数、friend
関数) - All other data members (static and non-static) 上記以外の(静的/非静的)データメンバ
Do not put large method definitions inline in the class definition. クラス定義の中では、大きなメソッドの定義をインラインに行わないでください。 Usually, only trivial or performance-critical, and very short, methods may be defined inline. 通常は、些細であるか、または、パフォーマンスが重要な、非常に短い関数のみをインラインで定義します。 See Inline Functions for more details. より詳細はインライン関数で述べます。
関数
入力と出力
The output of a C++ function is naturally provided via a return value and sometimes via output parameters (or in/out parameters). C++関数の出力は、通常は戻り値経由で提供されますが、しばしば出力用(もしくは入出力用)の引数も使われます。
Prefer using return values over output parameters: they improve readability, and often provide the same or better performance. 出力用引数よりは素直な戻り値を用いる方が望ましいです。 可読性が向上し、パフォーマンスも同等以上に得られます。
Prefer to return by value or, failing that, return by reference. 戻り値はできるだけ値返しで返し、何らかの事情によりそれができないときは参照返しにします。 Avoid returning a raw pointer unless it can be null. 戻り値がnull参照となりうる場合を除き、参照を生のポインタで返すことは避けてください。
Parameters are either inputs to the function, outputs from the
function, or both.
関数における引数は、関数への入力用、関数からの出力用、あるいはそれらの両方を行う入出力用のいずれかに分類されます。
Non-optional input parameters should usually be values
or const
references, while non-optional output and
input/output parameters should usually be references (which cannot be null).
関数に渡す入力用引数のうち、入力必須とするものは通常は値渡しかconst
参照渡しで宣言します。出力用および入出力用引数で必須のものは、通常は非const
参照渡し(nullを不可とするため)で宣言します。
Generally, use std::optional
to represent optional by-value
inputs, and use a const
pointer when the non-optional form would
have used a reference.
入力用引数のうち、入力を必須としないものについては、std::optional
で包んで値渡しするか、(参照渡しをしたいのであれば)const
ポインタを使います。
Use non-const
pointers to represent
optional outputs and optional input/output parameters.
出力用および入出力用引数のうち、必須としないものには非const
ポインタを使います。
Avoid defining functions that require a reference parameter to outlive the call. 関数引数で渡される参照に対して、その関数から処理が戻った後にも寿命が存続することを要求するような関数を定義することは避けてください。 In some cases reference parameters can bind to temporaries, leading to lifetime bugs. 参照引数には一時オブジェクトを束縛できる場合があり、そのような場合において寿命に関するバグにつながってしまうためです。 Instead, find a way to eliminate the lifetime requirement (for example, by copying the parameter), or pass retained parameters by pointer and document the lifetime and non-null requirements. かわりに、引数の寿命に関する要件を排除する方法をとる(例えば内部で引数をコピーしておく等)か、もしくは、その引数をポインタ渡しとして、寿命と非null性に関する要件をドキュメントに残すようにしてください。
When ordering function parameters, put all input-only parameters before any output parameters. 関数引数の順番を決めるときは、すべての入力専用引数を、いかなる出力用引数よりも先に配置します。 In particular, do not add new parameters to the end of the function just because they are new; place new input-only parameters before the output parameters. 特に、関数に後から新しい引数を追加するときでも、新しく追加することを理由に最後に配置するようなことは避けてください。 つまり、入力専用引数が新しくても、出力引数よりは前に並べてください。 This is not a hard-and-fast rule. とはいえ、これらの引数の並び順は絶対厳守のルールというわけでもありません。 Parameters that are both input and output muddy the waters, and, as always, consistency with related functions may require you to bend the rule. 入力と出力の両方の役割を持つ引数は、明確な線引きが難しいものもあります。 また、関連する関数との一貫性を保つためにはこのルールを曲げる必要があるかもしれません。 Variadic functions may also require unusual parameter ordering. あるいは、可変長引数関数においても、通常とは異なる引数順序で並べることを必要とするでしょう。
関数は短く
Prefer small and focused functions. 関数は、短く、焦点を絞ります。
We recognize that long functions are sometimes appropriate, so no hard limit is placed on functions length. 時には、関数が長くとも、それが適切なこともありますので、関数の長さに関する固定の限界値は定めません。 If a function exceeds about 40 lines, think about whether it can be broken up without harming the structure of the program. 通常は、だいたい40行程度を超えたくらいで、プログラムの構造を害さずに、関数を分割することができないか考えてみてください。
Even if your long function works perfectly now, someone modifying it in a few months may add new behavior. ある長い関数が現時点では完璧に動いていたとしても、数ヶ月後に誰かがそれを変更して、新しい動作を足すかもしれません。 This could result in bugs that are hard to find. その結果として、見つけるのが困難なバグに繋がるかもしれません。 Keeping your functions short and simple makes it easier for other people to read and modify your code. 関数を短くシンプルに保っておくことで、コードが、より読みやすく、より変更しやすくなるでしょう。 Small functions are also easier to test. また、小さい関数は、その分テストも容易です。
You could find long and complicated functions when working with some code. 作業をしていると、長く複雑なコードに出くわすことがあります。 Do not be intimidated by modifying existing code: if working with such a function proves to be difficult, you find that errors are hard to debug, or you want to use a piece of it in several different contexts, consider breaking up the function into smaller and more manageable pieces. 既存のコードを変更することに怯えないでください。 そのコードを取り扱うのが困難であると感じたとき、エラーをデバッグすることが困難であると感じたときとき、あるいは関数の一部分だけを切り出して別のところで使いたいと思ったとき、その関数をより小さく扱いやすい複数のピースに分割できないか検討してみてください。
関数のオーバーロード
Use overloaded functions (including constructors) only if a reader looking at a call site can get a good idea of what is happening without having to first figure out exactly which overload is being called. コンストラクタを含め、関数をオーバーロードするときは、コード読者が呼び出し元のコードを読んだときに、具体的にどの関数が呼び出されるのか正確に把握せずとも、何が行われるのか予想できるようにしてください。
You may write a function that takes a
const
std::string&
and overload it with another that
takes const char*
.
たとえば、const std::string&
を引数にとる関数とconst char*
を引数にとる関数とを書くことができます。
However, in this case consider
std::string_view
instead.
(もっとも、このケースでは、かわりにstd::string_view
を検討すべきです。)
class MyClass { public: void Analyze(const std::string &text); void Analyze(const char *text, size_t textlen); };
Overloading can make code more intuitive by allowing an identically-named function to take different arguments. 関数のオーバーロードを用いると、同じ関数名で異なる引数を受け入れる関数を定義することができるため、コードをより直感的にすることができます。 It may be necessary for templatized code, and it can be convenient for Visitors. この機能は、テンプレート化されたコードにおいて必須とされたり、また、ビジターパターンを実装するときにも同様に役立ったりします。
Overloading based on const
or ref qualification may make utility
code more usable, more efficient, or both.
(See TotW 148 for more.)
メンバ関数におけるconst
修飾子や参照修飾子によるオーバーロードは、ユーティリティコードをより有用なものにしたり、効率的にしたり、またその両方の恩恵をもたらしたりします。
(詳しくは TotW 148 を見てください)
If a function is overloaded by the argument types alone, a reader may have to understand C++'s complex matching rules in order to tell what's going on. 関数が引数の型だけでオーバーロードされているとき、コードの読者がC++の複雑なマッチングルールを理解していないと、何が起こるのかわからないかもしれません。 Also many people are confused by the semantics of inheritance if a derived class overrides only some of the variants of a function. また、複数のオーバーロードを持つ仮想関数について、派生先のクラスで部分的にオーバーライドした場合の挙動は、多くの人を困惑させています。
You may overload a function when there are no semantic differences between variants. 関数は、同じ関数名をもつオーバーロード定義同士の間で互いに意味上の違いがないときに限り、オーバーロードしてもかまいません。 These overloads may vary in types, qualifiers, or argument count. これらのオーバーロードの中には、型が異なったり、修飾子が異なったり、引数の数が異なったりと、様々なものを含んでいます。 However, a reader of such a call must not need to know which member of the overload set is chosen, only that something from the set is being called. しかし、それを呼び出す側のコードの読者は、当該オーバーロードのセットのうちのいずれかが呼ばれていることまでわかればよく、具体的にどれが選ばれるのかを知る必要はありません。 If you can document all entries in the overload set with a single comment in the header, that is a good sign that it is a well-designed overload set. あるオーバーロードのセットについてヘッダーファイルでコメントを書くとき、すべてのオーバーロードについてまとめて1つのコメントで賄えたとしたら、そのことは、その関数オーバーロードのセットをうまくデザインできている証です。
デフォルト引数
Default arguments are allowed on non-virtual functions when the default is guaranteed to always have the same value. 関数が仮想関数ではなく、かつ、常にその値が同じ値になることを保証する場合に限って、関数引数にデフォルト値を与えてもかまいません。 Follow the same restrictions as for function overloading, and prefer overloaded functions if the readability gained with default arguments doesn't outweigh the downsides below. 関数のオーバーロードと同様の制限に従ってください。 また、デフォルト引数を用いることによって得られる可読性よりも、後述するデメリットの方が勝るような場合は、かわりに関数のオーバーロードを使いましょう。
Often you have a function that uses default values, but occasionally you want to override the defaults. 普段はデフォルトの値のまま使っている関数でも、時々別の値に変えて呼び出したいことがあります。 Default parameters allow an easy way to do this without having to define many functions for the rare exceptions. 引数にデフォルト値を与えることによって、このような"時々"のための関数をたくさん定義することなく、簡単にやりたいことを実現できます。 Compared to overloading the function, default arguments have a cleaner syntax, with less boilerplate and a clearer distinction between 'required' and 'optional' arguments. この方法は、関数をオーバーロードする場合に比べて、文法的にきれいで、定型文も少なく済み、さらに、各引数が必須なのか任意なのかをより明確に区別することができます。
Defaulted arguments are another way to achieve the semantics of overloaded functions, so all the reasons not to overload functions apply. 引数のデフォルト値を与える手法は、意味的には関数のオーバーロードの代替手段であるため、すべての関数をオーバーロードしない理由も適用されます。
The defaults for arguments in a virtual function call are determined by the static type of the target object, and there's no guarantee that all overrides of a given function declare the same defaults. 仮想関数呼び出し時の引数のデフォルト値は、ターゲットとするオブジェクトの静的な型に基づいて決定されますが、すべてのオーバーライドにおいて同じ値が宣言されている保証がありません。
Default parameters are re-evaluated at each call site, which can bloat the generated code. 引数のデフォルト値は、すべての呼び出し元で再評価されます。このため、生成されるコードのサイズが予想以上にふくれあがることがあります。 Readers may also expect the default's value to be fixed at the declaration instead of varying at each call. また、コードの読者も、関数の呼び出しごとにデフォルト値が変わるのではなく、宣言時点で決まった値に固定されていると考えるかもしれません。
Function pointers are confusing in the presence of default arguments, since the function signature often doesn't match the call signature. 関数ポインタを引数のデフォルト値として与えると、その記述の仕方に困惑するかもしれません。関数のシグネチャはしばしば、呼び出し時のシグネチャと一致しないことがあるからです。 Adding function overloads avoids these problems. 関数のオーバーロードであれば、これらの問題は回避することができます。
Default arguments are banned on virtual functions, where
they don't work properly, and in cases where the specified
default might not evaluate to the same value depending on
when it was evaluated.
仮想関数に対する引数のデフォルト値は、意図に反する動作をすることがあるため、禁止とします。また、評価されるタイミング次第で異なる値になりうる引数のデフォルト値も禁止します。
(For example, don't write
void
f(int n = counter++);
.)
(たとえば、void f(int n = counter++);
のようなコードは書いてはいけません。)
In some other cases, default arguments can improve the readability of their function declarations enough to overcome the downsides above, so they are allowed. それら以外のケースにおいて、デフォルト値を与えることで、そのデメリットを差し引いても関数宣言の可読性を高められる場合には、関数引数にデフォルト値を与えてもかまいません。 When in doubt, use overloads. 迷ったときには、関数のオーバーロードを選択してください。
戻り値の型を後置する関数宣言構文
Use trailing return types only where using the ordinary syntax (leading return types) is impractical or much less readable. 戻り値の型を後置する関数宣言の構文は、従来からある通常の関数宣言構文(戻り値の型に始まる構文)を使うのが非現実的であったり、あるいは、その構文では可読性が非常に劣ってしまう場合に限定して用います。
C++ allows two different forms of function declarations. C++の関数宣言には、2種類の異なる構文が存在します。 In the older form, the return type appears before the function name. For example: 従来からある構文では、次のように、戻り値の型を関数名の前に記述します。
int foo(int x);
The newer form uses the auto
keyword before the function name and a trailing return type after
the argument list.
新しい形の構文では、まずauto
キーワードを関数名の前に置き、関数名、引数リストと続けた後に戻り値の型を書きます。
For example, the declaration above could
equivalently be written:
例として、先ほどの宣言と等価な宣言は次のように書けます。
auto foo(int x) -> int;
The trailing return type is in the function's scope.
後置される戻り値の型は関数内スコープになります。
This doesn't
make a difference for a simple case like int
but it matters
for more complicated cases, like types declared in class scope or
types written in terms of the function parameters.
int
のような単純なケースではこれらの間に違いはありませんが、より複雑な場合、たとえば、型がクラススコープで宣言されている場合や、型が関数の引数リスト内の語によって表現される場合は、この違いが重要になってきます。
Trailing return types are the only way to explicitly specify the return type of a lambda expression. 戻り値の型を後置する構文は、ラムダ式の戻り値の型を明示できる唯一の方法です。 In some cases the compiler is able to deduce a lambda's return type, but not in all cases. コンパイラによってラムダ式の戻り値の型を推論できる場合もありますが、常に可能というわけではありません。 Even when the compiler can deduce it automatically, sometimes specifying it explicitly would be clearer for readers. あるいは、コンパイラによって自動的に型が推論できる場合であっても、型を明示的に記述することで、コードの可読性が高まる場合もあります。
Sometimes it's easier and more readable to specify a return type after the function's parameter list has already appeared. 通常の関数においても、戻り値の型を引数リストの後に置くことで、コードがより簡単になり、可読性が高まることがあります。 This is particularly true when the return type depends on template parameters. これは、特に、戻り値の型がテンプレートパラメータによって決まる場合が当てはまります。 For example: 次に例を示します。
template <typename T, typename U> auto add(T t, U u) -> decltype(t + u);versus これに比べて、次のコードはどうでしょうか。
template <typename T, typename U> decltype(declval<T&>() + declval<U&>()) add(T t, U u);
Trailing return type syntax is relatively new and it has no analogue in C++-like languages such as C and Java, so some readers may find it unfamiliar. 戻り値の型を後置する関数宣言構文は、比較的新しく、C言語やJavaなどの他のC++系言語を見ても類似の構文がありません。このため、コードの読者にとって馴染みが薄いかもしれません。
Existing code bases have an enormous number of function declarations that aren't going to get changed to use the new syntax, so the realistic choices are using the old syntax only or using a mixture of the two. 既存のコードベースには既に膨大な数の関数宣言がありますが、それらのすべてが新しい構文に置き換わることはありません。 このため、現実的な選択肢は、古い構文だけを使い続けるのか、新旧両方の構文を混ぜて使っていくかのどちらかです。 Using a single version is better for uniformity of style. そして、スタイルの一様性を保つためには、1種類だけを使うことが望ましいです。
In most cases, continue to use the older style of function declaration where the return type goes before the function name. ほとんどの場合において、従来ながらの、戻り値の型を関数名の前に書く構文を使い続けてください。 Use the new trailing-return-type form only in cases where it's required (such as lambdas) or where, by putting the type after the function's parameter list, it allows you to write the type in a much more readable way. 新しい、戻り値の型を後置する関数宣言構文は、ラムダ式などにおいてその構文を必須とする場合や、あるいは、引数リストの後に戻り値の型を置くことによって型が大幅に読みやすくなる場合に限って使用してください。 The latter case should be rare; it's mostly an issue in fairly complicated template code, which is discouraged in most cases. とはいえ、後者の状況はレアケースなはずです。そのような状況のほとんどが、そのテンプレートコードが複雑すぎることに起因しており、通常、そのような複雑なテンプレートは非推奨だからです。
Google特有のマジック
There are various tricks and utilities that we use to make C++ code more robust, and various ways we use C++ that may differ from what you see elsewhere. Googleでは、C++のコードをより堅牢にするために、様々なトリックやユーティリティが使われています。 これらの手法の中には、一般的なC++の使い方とは異なる部分があるかもしれません。
オブジェクトの所有権とスマートポインタ
Prefer to have single, fixed owners for dynamically allocated objects. 動的に確保されるオブジェクトは、単一の、固定された所有者に属するようにします。 Prefer to transfer ownership with smart pointers. オブジェクトの所有権を移すときには、スマートポインタを使ってそれを表現します。
"Ownership" is a bookkeeping technique for managing dynamically allocated memory (and other resources). 「所有権」とは、動的に確保されるメモリ(や、その他のリソース)を管理するための帳簿的なテクニックの一つです。 The owner of a dynamically allocated object is an object or function that is responsible for ensuring that it is deleted when no longer needed. 動的に確保されるオブジェクトは、1つのオブジェクトもしくは1つの関数に所有され、その所有者であるオブジェクトや関数は、所有するオブジェクトが不要になったとき、確実にそれを解放する責任を負います。 Ownership can sometimes be shared, in which case the last owner is typically responsible for deleting it. 所有権は複数の所有者の間で共有されることもあり、この場合には、最後の所有者が解放の責任を負うのが典型的です。 Even when ownership is not shared, it can be transferred from one piece of code to another. あるいは、所有権を共有まではせずとも、あるコードから他のコードへと、オブジェクトの所有権が移されることもあります。
"Smart" pointers are classes that act like pointers,
e.g., by overloading the *
and
->
operators.
「スマートポインタ」とは、ポインタのように振る舞うクラスの総称で、たとえば、演算子*
と->
をオーバーロードしています。
Some smart pointer types
can be used to automate ownership bookkeeping, to ensure
these responsibilities are met.
スマートポインタの中には、所有権管理を自動的に行い、解放責任を確実に果たすために使えるものがあります。
std::unique_ptr
is a smart pointer type
introduced in C++11, which expresses exclusive ownership
of a dynamically allocated object; the object is deleted
when the std::unique_ptr
goes out of scope.
std::unique_ptr
は、C++11で導入されたスマートポインタのひとつで、動的確保されるオブジェクトに対して排他的な所有権を持つことを表現するために使われます。
std::unique_ptr
によって所有されるオブジェクトは、std::unique_ptr
がスコープ外となったときに自動的に解放されます。
It cannot be copied, but can be moved to
represent ownership transfer.
std::unique_ptr
はコピーできませんが、所有権を移すためにムーブすることはできます。
std::shared_ptr
is a smart pointer type
that expresses shared ownership of
a dynamically allocated object.
std::shared_ptr
は、動的確保されたオブジェクトの所有権を他と共有することを表現するために使われるスマートポインタです。
std::shared_ptr
s
can be copied; ownership of the object is shared among
all copies, and the object is deleted when the last
std::shared_ptr
is destroyed.
std::shared_ptr
はコピーすることができ、オブジェクトの所有権は、それらのすべてのコピー同士の間で共有されます。最後のstd::shared_ptr
インスタンスが破壊されるときに、共有しているオブジェクトが解放されます。
- It's virtually impossible to manage dynamically allocated memory without some sort of ownership logic. 所有権の概念がなければ、動的確保されるメモリを管理するのは実質不可能でしょう。
- Transferring ownership of an object can be cheaper than copying it (if copying it is even possible). オブジェクト自体をコピーすることに比べて、所有権の移動は低コストに済みます。
- Transferring ownership can be simpler than 'borrowing' a pointer or reference, because it reduces the need to coordinate the lifetime of the object between the two users. 参照やポインタでオブジェクトを借りるよりも、所有権を移してしまう方が、それらを扱う二者間でオブジェクトの生存期間に関する認識をあわせやすく、プログラムの構造をシンプルにできます。
- Smart pointers can improve readability by making ownership logic explicit, self-documenting, and unambiguous. スマートポインタを用いてオブジェクトの所有権に関するロジックを明示的に表現することで、それ自体がドキュメントの役割を果たし、コードの可読性が高まります。
- Smart pointers can eliminate manual ownership bookkeeping, simplifying the code and ruling out large classes of errors. スマートポインタを用いることで、オブジェクトの所有権を手作業で管理する手間から解放されます。これによって、コードがシンプルになり、所有権にまつわる様々な種類のバグを埋め込む可能性もまとめて排除できます。
-
For
const
objects, shared ownership can be a simple and efficient alternative to deep copying.const
なオブジェクトにおいて、その所有権を共有することは、オブジェクトをディープコピーするのに比べて、シンプルかつ効率的な代替手段となります。
- Ownership must be represented and transferred via pointers (whether smart or plain). オブジェクトの所有権の概念を取り扱うときは、ポインタがスマートであるなしに関わらず、いずれにせよ、何らかのポインタを経由して取り扱うことになります。 Pointer semantics are more complicated than value semantics, especially in APIs: you have to worry not just about ownership, but also aliasing, lifetime, and mutability, among other issues. しかし、ポインタの概念は、値を直接取り扱うよりも複雑です。とりわけAPIの層では、所有権についてだけではなく、エイリアシングや、寿命や、変更可能性など、様々な点について考慮しなければならなくなります。
- The performance costs of value semantics are often overestimated, so the performance benefits of ownership transfer might not justify the readability and complexity costs. 値を直接扱う場合のパフォーマンスコストは、しばしば、実際にかかるコストよりも高めに見積もられがちです。このため、パフォーマンスコストの低減を目当てに所有権管理の方法を採用しても、そのメリットは、コードの可読性低下や複雑化などのデメリットに見合わないかもしれません。
- APIs that transfer ownership force their clients into a single memory management model. 所有権を移動させるようなAPIは、ユーザーに単一のメモリ管理モデルを採用することを強制します。
- Code using smart pointers is less explicit about where the resource releases take place. スマートポインタを扱うコードは、リソースの解放がどこで行われるのか、コードの見た目からわかりにくくなります。
-
std::unique_ptr
expresses ownership transfer using move semantics, which are relatively new and may confuse some programmers.std::unique_ptr
は、ムーブで所有権の移動を表現しますが、このムーブの概念自体が新しく、プログラマーによっては困惑してしまうかもしれません。 - Shared ownership can be a tempting alternative to careful ownership design, obfuscating the design of a system. 注意深く所有権に関する設計を行うかわりに、単に所有権を共有させて済ませてしまうやり方は、プログラマーの目には、魅惑的な代替手段であるかのように映るかもしれません。しかし、それを選んでしまうと、システム全体のデザインがわかりにくくなってしまいます。
- Shared ownership requires explicit bookkeeping at run-time, which can be costly. 所有権を共有するためには、実行時にそれを管理する必要があります。これによって、思いのほかプログラムの性能が低下してしまうかもしれません。
- In some cases (e.g., cyclic references), objects with shared ownership may never be deleted. 循環参照を作ってしまった場合など、所有権を共有したオブジェクトが一切解放されなくなってしまうことがあります。
- Smart pointers are not perfect substitutes for plain pointers. スマートポインタは、生のポインタで実現できることを完全には代替しません。
If dynamic allocation is necessary, prefer to keep
ownership with the code that allocated it.
動的にオブジェクトを確保する必要があるときは、それを行うコードにオブジェクトの所有権を持たせるようにしましょう。
If other code
needs access to the object, consider passing it a copy,
or passing a pointer or reference without transferring
ownership.
他のコードからそのオブジェクトにアクセスしたいときは、オブジェクトをコピーして渡す方法か、所有権を移さずにポインタか参照を渡す方法を検討してください。
Prefer to use std::unique_ptr
to
make ownership transfer explicit.
所有権を移すときは、それを明示的に表現するために、std::unique_ptr
を使いましょう。
For example:
以下に例を示します。
std::unique_ptr<Foo> FooFactory(); void FooConsumer(std::unique_ptr<Foo> ptr);
Do not design your code to use shared ownership
without a very good reason.
オブジェクトの所有権を共有させる設計は、非常に良い理由がない限り、避けてください。
One such reason is to avoid
expensive copy operations, but you should only do this if
the performance benefits are significant, and the
underlying object is immutable (i.e.,
std::shared_ptr<const Foo>
).
ここでいう理由には、たとえばオブジェクトをコピーする高いコストを避けたいから、といったものが挙げられます。しかし、その場合であっても、そのパフォーマンス上のメリットが非常に大きく、かつ、対象となるオブジェクトが不変である(すなわち、std::shared_ptr<const Foo>
)場合に限定すべきです。
If you
do use shared ownership, prefer to use
std::shared_ptr
.
これらを踏まえて、所有権を共有させることを選ぶ場合は、std::shared_ptr
を使いましょう。
Never use std::auto_ptr
.
std::auto_ptr
の使用は禁止します。
Instead, use
std::unique_ptr
.
かわりに、std::unique_ptr
を使ってください。
cpplint
Use cpplint.py
to detect style errors.
cpplint.py
を使ってスタイルを診断しましょう。
cpplint.py
is a tool that reads a source file and identifies many
style errors.
cpplint.py
は、ソースファイルを読み込ませると、スタイルに関するたくさんのエラーを識別してくれるツールです。
It is not perfect, and has both false
positives and false negatives, but it is still a valuable
tool.
このツールは完璧ではなく、ときに誤検出したり、問題を見逃したりもしますが、それでも十分に有用であることは間違いありません。
False positives can be ignored by putting
// NOLINT
at the end of the line or
// NOLINTNEXTLINE
in the previous line.
偽陽性の誤判定がなされる場合は、行末に// NOLINT
を書くか、直前の行に// NOLINTNEXTLINE
を書くことで、無視させることができます。
Some projects have instructions on
how to run cpplint.py
from their project
tools.
プロジェクトによっては、プロジェクトツールでcpplint.py
を起動する手順が用意されている場合があります。
If the project you are contributing to does not,
you can download
cpplint.py
separately.
それ以外の場合、cpplint.py
から個別にダウンロードすることができます。
その他のC++の機能
右辺値参照(rvalue reference)
Use rvalue references only in certain special cases listed below. 右辺値参照は、以下で説明するような特殊な場合に限り使用します。
Rvalue references
are a type of reference that can only bind to temporary
objects.
右辺値参照(rvalue reference)とは、一時オブジェクトのみを束縛する参照のことです。
The syntax is similar to traditional reference
syntax.
その文法は従来からある参照の文法に似ています。
For example, void f(std::string&& s);
declares a function whose argument is an
rvalue reference to a std::string
.
たとえば、void f(string&& s);
は、std::string
への右辺値参照を引数にとる関数を宣言しています。
When the token '&&' is applied to
an unqualified template argument in a function
parameter, special template argument deduction
rules apply.
関数の仮引数において、修飾子のないテンプレート引数に &&
が使われた場合、特殊なテンプレート引数推論ルールが適用されます。
Such a reference is called a forwarding reference.
このような特殊な参照は、「転送参照(Forwarding Reference)」と呼ばれます。(訳注:従来はユニバーサル参照(Universal Reference)と呼ばれていましたが、C++17で転送参照という語が定められました)
-
Defining a move constructor (a constructor taking
an rvalue reference to the class type) makes it
possible to move a value instead of copying it.
ムーブコンストラクタ(そのクラス型への右辺値参照をとるコンストラクタ)を定義すると、値をコピーするかわりにムーブすることができるようになります。
If
v1
is astd::vector<std::string>
, for example, thenauto v2(std::move(v1))
will probably just result in some simple pointer manipulation instead of copying a large amount of data. たとえば、変数v1
がstd::vector<std::string>
であるとき、auto v2(std::move(v1))
とすれば、おそらく、大量のデータのコピーを発生させることなく、単なるポインタ操作だけで済まされるでしょう。 In many cases this can result in a major performance improvement. このような動作は、多くの場合において、大きなパフォーマンス向上に繋がります。 - Rvalue references make it possible to implement types that are movable but not copyable, which can be useful for types that have no sensible definition of copying but where you might still want to pass them as function arguments, put them in containers, etc. 右辺値参照によって、ムーブ可能かつコピー不可な型を実装することができるようになります。型に意味のあるコピーを定義できないときであっても、ムーブ操作によって、関数の引数に渡したり、コンテナに格納したり等、その他様々な操作が実現できるようになります。
-
std::move
is necessary to make effective use of some standard-library types, such asstd::unique_ptr
.std::move
は、たとえばstd::unique_ptr
のような、いくつかの標準ライブラリを有効に使うために必須です。 - Forwarding references which use the rvalue reference token, make it possible to write a generic function wrapper that forwards its arguments to another function, and works whether or not its arguments are temporary objects and/or const. 右辺値参照と同じ記号を使う転送参照を用いると、引数を他の関数に転送する汎用的なラッパー関数を書くことができます。 これは、引数が一時オブジェクトであるなしに、また、constであるなしに関わらず正しく動作します。 This is called 'perfect forwarding'. これを「完全転送(perfect forwarding)」と呼びます。
- Rvalue references are not yet widely understood. 右辺値参照は、まだ広く理解されていません。 Rules like reference collapsing and the special deduction rule for forwarding references are somewhat obscure. まして、参照の折りたたみ(reference collapsing)や転送参照に対する特殊な型推論ルールなどに至っては、おぼろげにしか理解されていません。
- Rvalue references are often misused. 右辺値参照は、しばしば間違った使い方で使われます。 Using rvalue references is counter-intuitive in signatures where the argument is expected to have a valid specified state after the function call, or where no move operation is performed. 右辺値参照は、その見た目に対して非直感的な動作をします。 たとえば、右辺値参照の引数を与えた関数呼び出しの後にも変数が有効な状態を持っていそうに見えたり、あるいは、逆に、右辺値参照に渡したにも関わらず、実際にはムーブ操作が行われなかったりします。
Do not use rvalue references (or apply the &&
qualifier to methods), except as follows:
右辺値参照(や、メソッドの&&
参照修飾子)は、以下の場合に限り使用できます。それ以外の場合は使ってはいけません。
- You may use them to define move constructors and move assignment operators (as described in Copyable and Movable Types) ムーブコンストラクタ・ムーブ代入演算子(詳細はコピー可能な型・ムーブ可能な型にて記載)を定義するためであれば、右辺値参照を使ってもかまいません。
-
You may use them to define
&&
-qualified methods that logically "consume"*this
, leaving it in an unusable or empty state.*this
を論理的に消費して、それ以降使用不可(あるいは空の状態)にするようなメソッドを定義するためであれば、右辺値修飾子(&&
修飾子)を使ってもかまいません。 Note that this applies only to method qualifiers (which come after the closing parenthesis of the function signature); if you want to "consume" an ordinary function parameter, prefer to pass it by value. ただし、あくまでメソッドの修飾子(関数シグネチャの閉じ丸括弧のあとに続く修飾子)にのみが認められるということに注意してください。関数引数を消費する場合については、単なる値渡しを用いる方が望ましいです。 -
You may use forwarding references in conjunction with
std::forward
, to support perfect forwarding. 完全転送(perfect forwarding)を実現するためにstd::forwardとあわせた転送参照を使ってもかまいません。 -
You may use them to define pairs of overloads, such as one taking
Foo&&
and the other takingconst Foo&
. オーバーロードの組を定義するためであれば、右辺値参照を使ってもかまいません。 たとえばFoo&&
を引数にとる関数オーバーロードとconst Foo&
を引数にとる関数オーバーロードを用意すると都合がよい場合があります。 Usually the preferred solution is just to pass by value, but an overloaded pair of functions sometimes yields better performance, for example if the functions sometimes don't consume the input. 通常は、単純な値渡しを採用するのがもっとも望ましいですが、このような関数オーバーロードを別々に用意することでパフォーマンスが向上する場合もあります。たとえば、関数常に入力を消費するとは限らないような場合です。 As always: if you're writing more complicated code for the sake of performance, make sure you have evidence that it actually helps. ただし、パフォーマンスの都合でコードを複雑化させるときは、いつものように、そのコード変更が実際にパフォーマンス向上に貢献しているという証拠を確かめるようにしてください。
フレンド
We allow use of friend
classes and functions,
within reason.
合理的な範囲内であれば、friend
クラスやfriend
関数を使ってもかまいません。
Friends should usually be defined in the same file so
that the reader does not have to look in another file to
find uses of the private members of a class.
フレンドに指定する対象は、通常は同じファイル内で定義してください。
そうすることで、コードの読者は、それらのフレンドがクラスのプライベートメンバをどう扱うのか、他のファイルに探しに行かずともよくなります。
A common use
of friend
is to have a
FooBuilder
class be a friend of
Foo
so that it can construct the inner state
of Foo
correctly, without exposing this
state to the world.
よくあるフレンドの使い方は、たとえば、FooBuilder
クラスをFoo
クラスのフレンドにして、Foo
の内部状態を全世界に晒すことなくFooBuilder
に正しく構築させられるようにするといったパターンです。
In some cases it may be useful to
make a unittest class a friend of the class it tests.
他にも、たとえば、クラスのユニットテストを担うクラスをフレンド指定しておくと便利なこともあります。
Friends extend, but do not break, the encapsulation
boundary of a class.
フレンドの仕組みは、クラスのカプセル化の境界を壊すことなく拡張することができます。
In some cases this is better than
making a member public
when you want to give only one
other class access to it.
ある特定のクラスにだけメンバへのアクセス権を与えたいとき、そのメンバを単にpublic
にするよりも、フレンドを用いる方が適切な場合があります。
However, most classes should
interact with other classes solely through their public
members.
ただし、原則としては、ほとんどのクラス間のやりとりはpublic
メンバを通してのみ行うようにしてください。
例外
We do not use C++ exceptions. 私たちはC++の例外を使いません。
- Exceptions allow higher levels of an application to decide how to handle "can't happen" failures in deeply nested functions, without the obscuring and error-prone bookkeeping of error codes. 例外の機構を用いると、深くネストされた関数の中で起きた起こらないはずの失敗について、不明瞭で取り違えやすい帳簿的なエラーコードを扱うことなく、アプリケーションのより高い層でその処理方法を決定できるようになります。
- Exceptions are used by most other modern languages. 例外機構は、多くの現代的な言語で採用されています。 Using them in C++ would make it more consistent with Python, Java, and the C++ that others are familiar with. C++でも例外を取り入れれば、PythonやJava、そして、多くの人にとって馴染みのある(例外を有効にした)C++との一貫性がより高まるでしょう。
- Some third-party C++ libraries use exceptions, and turning them off internally makes it harder to integrate with those libraries. サードパーティ製のC++ライブラリには例外を使用しているものがあります。 内部的に例外機構自体を無効にしてしまうと、そのようなライブラリを統合するのが難しくなってしまいます。
-
Exceptions are the only way for a constructor to
fail.
例外は、コンストラクタ内の失敗を伝えられる唯一の手段です。
We can simulate this with a factory function or
an
Init()
method, but these require heap allocation or a new "invalid" state, respectively. 例外を使わなくても、ファクトリ関数やInit()
メソッドを用いれば、初期化時のエラーを伝えることができるようにはなりますが、これらの代替手段はヒープの確保を必要としたり、あるいは、別途オブジェクトが不正な状態であることを表せる仕組みを必要としたりします。 - Exceptions are really handy in testing frameworks. 例外は、テストフレームワークにとって、非常に手頃な手段といえます。
-
When you add a
throw
statement to an existing function, you must examine all of its transitive callers. 既存の関数にthrow
文を追加するのであれば、その関数の呼び出し元について、推移的なものまで含めて、すべて調べ、 Either they must make at least the basic exception safety guarantee, or they must never catch the exception and be happy with the program terminating as a result. それらの呼び出し元のすべてが、最低限の基本的な例外安全保証を満たしているか、あるいは、例外を一切キャッチせず関数が途中で終了しても問題ないような作りになっているかのいずれかであることを確認しなければなりません。 For instance, iff()
callsg()
callsh()
, andh
throws an exception thatf
catches,g
has to be careful or it may not clean up properly. 例として、たとえばf()
がg()
を呼び、g()
がh()
を呼んでいて、h
が例外を投げf
でキャッチしたときのことを考えてみましょう。 この場合は、g
においても慎重に確認しないと、g
が確保したリソースがきちんと解放されなくなってしまうかもしれません。 - More generally, exceptions make the control flow of programs difficult to evaluate by looking at code: functions may return in places you don't expect. より一般に、例外が使われているプログラムは、コード上の見た目からプログラムの流れを評価することが難しくなります。例外が投げられると、関数の途中の思いもしないところから、唐突にその呼び出し元へと処理が戻ってしまいます。 This causes maintainability and debugging difficulties. このことは、メンテナンス性の低下や、デバッグの難しさにつながります。 You can minimize this cost via some rules on how and where exceptions can be used, but at the cost of more that a developer needs to know and understand. これらのコストを低減するためには、例外の使い方に関するルール(どこで・どのようにであれば例外を使ってもよいのか)を設ければ、そのコストを最小化できそうにも思えます。しかし、今度はその代償として、すべての開発者がそれらのルールを知って理解しておくことが必要になり、このことがさらに高いコストとなってしまうかもしれません。
- Exception safety requires both RAII and different coding practices. 例外安全なプログラムを書くためには、RAIIのコーディング手法と、それと異なる手法との両方を必要とします。 Lots of supporting machinery is needed to make writing correct exception-safe code easy. 正しく例外安全なコードを簡単に書けるようにするためには、多くのサポート機構を必要とします。 Further, to avoid requiring readers to understand the entire call graph, exception-safe code must isolate logic that writes to persistent state into a "commit" phase. さらに、コード読者が呼び出しグラフ全体を理解していなければならないような事態を避けるために、例外安全なコードでは、永続的な状態を書き込むロジックをコミット段階として分割しておかなければなりません。 This will have both benefits and costs (perhaps where you're forced to obfuscate code to isolate the commit). これには、良い面もありますが悪い面もあります。 おそらく、コミットを行うコードを分離するために、コードが複雑化するのを避けられないはずです。 Allowing exceptions would force us to always pay those costs even when they're not worth it. 例外を使うことを認めたなら、たとえ価値が見合わないところでも、常にこれらのコストを払い続けなければなりません。
- Turning on exceptions adds data to each binary produced, increasing compile time (probably slightly) and possibly increasing address space pressure. 例外を有効にすると、実行バイナリに追加のデータが生成されます。これによって、(わずかながら)コンパイルの所要時間が延びたり、使用可能なアドレス空間が圧迫されたりするかもしれません。
- The availability of exceptions may encourage developers to throw them when they are not appropriate or recover from them when it's not safe to do so. 例外を使用可能にしていると、本来適切でない箇所で例外を投げたり、本来安全ではない回復を試みたりすることを開発者に促してしまうかもしれません。 For example, invalid user input should not cause exceptions to be thrown. たとえば、ユーザーの入力が不正だったとしても、それは例外を投げるべき事柄ではありません。 We would need to make the style guide even longer to document these restrictions! 例外を使うことを認めるためには、そのようなことまでこのガイドに記述することになるため、このスタイルガイドをもっともっと長くしなければなりません!
On their face, the benefits of using exceptions outweigh the costs, especially in new projects. 表面的には、特に新規のプロジェクトであれば、例外を有効にするメリットがコストより上回るでしょう。 However, for existing code, the introduction of exceptions has implications on all dependent code. しかし、その一方で、既存のコードにおいて例外を導入しようとすると、そのすべての依存コードに影響を与えてしまいます。 If exceptions can be propagated beyond a new project, it also becomes problematic to integrate the new project into existing exception-free code. このため、もし何らか新しいプロジェクトを採用したい場合でも、そのプロジェクトから例外が伝播してくる可能性があるとなると、それらを既存の例外未使用のコードと統合することは難しい問題になります。 Because most existing C++ code at Google is not prepared to deal with exceptions, it is comparatively difficult to adopt new code that generates exceptions. そして、Googleの既存のC++コードは、そのほとんどにおいて、例外を扱う準備ができていません。このため、新しいコードが例外を生成するのであれば、それらを採用することは比較的困難なことです。
Given that Google's existing code is not exception-tolerant, the costs of using exceptions are somewhat greater than the costs in a new project. 現状のGoogleの既存のコードは例外を扱えないため、これらのコードで新たに例外を扱うコストは、新規のプロジェクトで例外を扱う場合に比べて、いくぶん大きなものになります。 The conversion process would be slow and error-prone. 例外への対応には時間がかかるでしょうし、ミスも起こりやすいでしょう。 We don't believe that the available alternatives to exceptions, such as error codes and assertions, introduce a significant burden. 私たちは、例外のかわりに、エラーコードやアサーションといった代替手段を使うことが、大きな負担をもたらすとは考えていません。
Our advice against using exceptions is not predicated on philosophical or moral grounds, but practical ones. このガイドで述べている例外に関するアドバイスは、哲学的あるいは道徳的な根拠によるものではなく、現実的な根拠に基づくものです。 Because we'd like to use our open-source projects at Google and it's difficult to do so if those projects use exceptions, we need to advise against exceptions in Google open-source projects as well. 私たちは、Googleでオープンソースプロジェクトを使いたいと考えています。しかし、新しいプロジェクトで例外が扱われているとそれを採用するのが難しくなってしまいます。このため、私たちは、Googleのオープンソースプロジェクトに対しても同様に例外を避けるようにアドバイスする必要があります。 Things would probably be different if we had to do it all over again from scratch. もし、最初からすべてのことをやり直すとしたら、何かが違っていたかもしれません。
This prohibition also applies to exception handling related
features such as
std::exception_ptr
and
std::nested_exception
.
これらの禁止ルールは、std::exception_ptr
、std::nested_exception
などの、例外処理に関する機能についても同様に適用します。
There is an exception to this rule (no pun intended) for Windows code. なお、例外についてはWindowsのコードに関する例外があります(ダジャレじゃないですよ)。
noexcept
Specify noexcept
when it is useful and correct.
有用かつ正しいときnoexcept
を指定します。
The noexcept
specifier is used to specify whether
a function will throw exceptions or not.
noexcept
指定子は、関数が例外を投げるか否かを指定するために使われます。
If an exception
escapes from a function marked noexcept
, the program
crashes via std::terminate
.
noexcept
とマークされた関数から例外が抜け出すと、プログラムは std::terminate
を介してクラッシュします。
The noexcept
operator performs a compile-time
check that returns true if an expression is declared to not
throw any exceptions.
noexcept
演算子は、与えられた式の宣言から、式が一切の例外を投げないと判定される場合に真となるコンパイル時のチェックを実行します。
-
Specifying move constructors as
noexcept
improves performance in some cases, e.g.,std::vector<T>::resize()
moves rather than copies the objects if T's move constructor isnoexcept
. ムーブコンストラクタをnoexcept
として指定すると、いくつかの場面でパフォーマンスが向上します。 たとえば、std::vector<T>::resize()
において、T
のムーブコンストラクタがnoexcept
指定されていれば、オブジェクトはコピーではなくムーブされます。 -
Specifying
noexcept
on a function can trigger compiler optimizations in environments where exceptions are enabled, e.g., compiler does not have to generate extra code for stack-unwinding, if it knows that no exceptions can be thrown due to anoexcept
specifier. 関数をnoexcept
として指定すると、例外が有効になっている環境でコンパイラによる最適化が働くかもしれません。 たとえば、関数が例外を投げないことがわかっているので、コンパイラはスタックアンワインド(スタックの巻き戻し)のための余分なコードを生成する必要がなくなります。
-
In projects following this guide
that have exceptions disabled it is hard
to ensure that
noexcept
specifiers are correct, and hard to define what correctness even means. このガイドに沿って例外を無効にしているプロジェクトでは、noexcept
指定子の正しさを確かめることは難しく、そもそも、その正しさとは何を意味するのかを定義することさえ困難です。 -
It's hard, if not impossible, to undo
noexcept
because it eliminates a guarantee that callers may be relying on, in ways that are hard to detect. ひとたびnoexcept
を指定してしまうと、後の変更でそれを取り消すことは、不可能ではないにせよ、困難な作業になることがあります。その関数の呼び出し元のコードがnoexcept
性に依存した実装を行っていた場合、noexcept
を取り消すことは、呼び出し元のコードが依存する前提条件を崩してしまうことになります。そして、このことを検出する手段にも乏しいためです。
You may use noexcept
when it is useful for
performance if it accurately reflects the intended semantics
of your function, i.e., that if an exception is somehow thrown
from within the function body then it represents a fatal error.
関数の意図するセマンティクスを正確に反映している場合、すなわち、もし例外が投げられたのであれば即それは致命的なエラーを表すという場合において、noexcept
がパフォーマンスに貢献するのであれば、これを使用してかまいません。
You can assume that noexcept
on move constructors
has a meaningful performance benefit.
ムーブコンストラクタのnoexcept
には、有意なパフォーマンス上の利点があると考えてかまいません。
If you think
there is significant performance benefit from specifying
noexcept
on some other function, please discuss it
with
your project leads.
それ以外の関数において、noexcept
によって大きなパフォーマンス上のメリットが得られると考える場合は、プロジェクトリーダーと相談して決めてください。
Prefer unconditional noexcept
if exceptions are
completely disabled (i.e., most Google C++ environments).
例外を完全に無効にしている環境(すなわち、ほとんどのGoogleのC++環境)では、条件のないnoexcept
を使ってください。
Otherwise, use conditional noexcept
specifiers
with simple conditions, in ways that evaluate false only in
the few cases where the function could potentially throw.
それ以外の場合は、シンプルな条件とともに条件付きのnoexcept
指定子を使用してください。
これらで指定する条件は、関数が潜在的に例外を投げる可能性のある少数のケースでのみ偽に評価されるようにします。
The tests might include type traits check on whether the
involved operation might throw (e.g.,
std::is_nothrow_move_constructible
for
move-constructing objects), or on whether allocation can throw
(e.g., absl::default_allocator_is_nothrow
for
standard default allocation).
この条件子におけるテストには、たとえば、呼び出す操作が例外を投げるか否か(ムーブ生成するオブジェクトに対するstd::is_nothrow_move_constructible
)や、メモリ確保操作が例外を投げるか否か(デフォルトのアロケート操作に対するabsl::default_allocator_is_nothrow
)などの、型トレイトに関するチェックなどが含まれるでしょう。
Note in many cases the only
possible cause for an exception is allocation failure (we
believe move constructors should not throw except due to
allocation failure), and there are many applications where it’s
appropriate to treat memory exhaustion as a fatal error rather
than an exceptional condition that your program should attempt
to recover from.
なお、多くの場合において、例外の唯一の原因はアロケーション(メモリやリソース確保)操作の失敗であることに注意してください。(ムーブコンストラクタはリソース確保操作の失敗以外で例外を投げるべきではありません。)
そして、このようなメモリの枯渇は、多くのアプリケーションにおいて、プログラムが回復を試みるべき状況ではなく、単なる致命的なエラーとして扱う方が適切であることも多いです。
Even for other
potential failures you should prioritize interface simplicity
over supporting all possible exception throwing scenarios:
instead of writing a complicated noexcept
clause
that depends on whether a hash function can throw, for example,
simply document that your component doesn’t support hash
functions throwing and make it unconditionally
noexcept
.
その他の潜在的に起こりえるエラーに関しても、考えられるすべての種類の例外シナリオに対応しようとするのではなく、インターフェースをシンプルに保つことを優先してください。たとえば、与えられたハッシュ関数が例外を投げるか否かを判定するための複雑なnoexcept
クロージャを書くのではなく、そのかわりに、その関数のドキュメント等で例外を投げるハッシュ関数をサポートしないことを表明し、単に、条件なしのnoexcept
指定子でマークしてしまうのもひとつのやり方です。
実行時型情報(RTTI)
Avoid using run-time type information (RTTI). 実行時型情報(RTTI)を使うのは避けます。
RTTI allows a
programmer to query the C++ class of an object at
run-time.
RTTI(Run-Time Type Information:実行時型情報)を用いると、プログラマーは実行時にオブジェクトのC++クラスを調べることができます。
This is done by use of typeid
or
dynamic_cast
.
これは、typeid
やdynamic_cast
を使うことで実現されます。
The standard alternatives to RTTI (described below) require modification or redesign of the class hierarchy in question. RTTIに対する標準的な代替手段(後述)を使おうとすると、対象とするクラスの階層構造の変更や再設計が必要になってしまいます。 Sometimes such modifications are infeasible or undesirable, particularly in widely-used or mature code. 既にそのコードが広く使われていたり成熟していたりすると、このような変更を施すことはもはや実現不可能であったり、あるいは望ましくないということがあります。
RTTI can be useful in some unit tests. RTTIはユニットテストにおいても有用な場面があります。 For example, it is useful in tests of factory classes where the test has to verify that a newly created object has the expected dynamic type. たとえば、ファクトリクラスをテストするとき、正しい型のオブジェクトが生成できていることを検証するためにはRTTIが使えるでしょう。 It is also useful in managing the relationship between objects and their mocks. また、オブジェクトとそのモックとの関係を管理するのにも有用です。
RTTI is useful when considering multiple abstract objects. RTTIは、多数の抽象オブジェクトを考えるときにも有用です。 Consider 以下の例を考えてみてください。
bool Base::Equal(Base* other) = 0; bool Derived::Equal(Base* other) { Derived* that = dynamic_cast<Derived*>(other); if (that == nullptr) return false; ... }
Querying the type of an object at run-time frequently means a design problem. 実行時に頻繁にオブジェクトの型を調べる必要が生じているのであれば、その事実は、プログラムに設計上の問題があることを示唆しています。 Needing to know the type of an object at runtime is often an indication that the design of your class hierarchy is flawed. 実行時にオブジェクトの型を知る必要があるということは、しばしば、クラス階層構造の設計に欠陥があることを示しています。
Undisciplined use of RTTI makes code hard to maintain. 無秩序にRTTIを濫用すると、コードのメンテナンスが困難になります。 It can lead to type-based decision trees or switch statements scattered throughout the code, all of which must be examined when making further changes. このようなコードにおいては、型に基づく決定木やswitch文がコード中のあちこちに散乱してしまいがちになり、さらに、将来コードを変更するときには、それらのすべてをチェックして回る必要に迫られます。
RTTI has legitimate uses but is prone to abuse, so you must be careful when using it. RTTIには妥当な使い方もありますが、やや逸脱した使われ方をする傾向にあります。RTTIを扱うときは十分注意してください。 You may use it freely in unittests, but avoid it when possible in other code. ユニットテストにおいては、自由にRTTIを使ってもかまいません。それ以外のコードにおいては可能な限り避けてください。 In particular, think twice before using RTTI in new code. 特に、新しいコードにおいては、RTTIを使おうとする前に、もう一度よく考えてみてください。 If you find yourself needing to write code that behaves differently based on the class of an object, consider one of the following alternatives to querying the type: もし、オブジェクトのクラスに応じて挙動を変えるコードを書く必要が生じたときでも、すぐにRTTIで型を調べようとせず、かわりに次のいずれかの代替手段が採用できないか検討してみてください。
- Virtual methods are the preferred way of executing different code paths depending on a specific subclass type. もっとも望ましいのは、仮想関数を用いることです。 仮想関数を使えば、派生クラス型に応じて実行するコードパスを変えられます。 This puts the work within the object itself. この方法であれば、必要な処理をそのオブジェクト自身に書くことができます。
- If the work belongs outside the object and instead in some processing code, consider a double-dispatch solution, such as the Visitor design pattern. 処理がオブジェクトの外にある何らかのコードに属している場合は、Visitorデザインパターンのような、二重ディスパッチを適用できることがあります。 This allows a facility outside the object itself to determine the type of class using the built-in type system. この方法であれば、オブジェクトの外にある機能において、組み込みの型システムを使って型を決定できます。
When the logic of a program guarantees that a given
instance of a base class is in fact an instance of a
particular derived class, then a
dynamic_cast
may be used freely on the
object.
プログラムのある地点において、プログラム構造上、ある基底クラスのインスタンスが、実際にはある特定の派生クラスのインスタンスであると保証できる場合は、そのオブジェクトに対してdynamic_cast
を使ってもかまいません。
Usually one
can use a static_cast
as an alternative in
such situations.
通常、このような場合においては、代替手段としてstatic_cast
を使うこともできます。
Decision trees based on type are a strong indication that your code is on the wrong track. 型による分岐が必要になったとき、その事実は、コードが間違った方向に進んでいることを強く示唆しています。
if (typeid(*data) == typeid(D1)) { ... } else if (typeid(*data) == typeid(D2)) { ... } else if (typeid(*data) == typeid(D3)) { ...
Code such as this usually breaks when additional subclasses are added to the class hierarchy. このようなコードは、大抵の場合、クラスの階層構造に新たな派生クラスが増えたときに破綻してしまいます。 Moreover, when properties of a subclass change, it is difficult to find and modify all the affected code segments. さらに、既存の派生クラスの性質が変わったときには、影響のあるすべてのコードを検索して変更しなければなりませんが、それらを正確に行うのは非常に難しいでしょう。
Do not hand-implement an RTTI-like workaround. なお、RTTIのようなものを自前で実装してはいけません。 The arguments against RTTI apply just as much to workarounds like class hierarchies with type tags. ここまでのRTTIに関する議論は、何らかの自作の仕組み、たとえばオブジェクトに型を表すタグをつけて識別する手法などであっても同様に当てはまります。 Moreover, workarounds disguise your true intent. その上、そのような回避策を採用すると、真の意図が見えにくくなってしまいます。
キャスト
Use C++-style casts
like static_cast<float>(double_value)
, or brace
initialization for conversion of arithmetic types like
int64_t y = int64_t{1} << 42
.
キャストには、static_cast<float>(double_value)
のようなC++スタイルのキャストを使います。
算術型の変換には、int64_t y = int64_t{1} << 42
のような、波括弧初期化を使います。
Do not use
cast formats like (int)x
unless the cast is to
void
.
void
へのキャストを除いて(int)x
の形のキャストは使ってはいけません。
You may use cast formats like T(x)
only when
T
is a class type.
なお、型T
がクラスであるときに限り、T(x)
のような形のキャストを使用してもかまいません。
C++ introduced a different cast system from C that distinguishes the types of cast operations. C++にはC言語とは異なるキャストシステムが導入されており、C++のキャストはその操作の種類によって区別されます。
The problem with C casts is the ambiguity of the operation;
sometimes you are doing a conversion
(e.g., (int)3.5
) and sometimes you are doing
a cast (e.g., (int)"hello"
).
C言語のキャストの問題点は、その操作が曖昧であることです。
これらは、ときには変換(例:(int)3.5
)であり、ときにはキャスト(例:(int)"hello"
)を表しています。
Brace
initialization and C++ casts can often help avoid this
ambiguity.
波括弧初期化とC++のキャストを使えば、このような曖昧さはなくなります。
Additionally, C++ casts are more visible when searching for
them.
加えて、C++のキャストを使っていれば、それら検索するときにも見つけやすくもなります。
The C++-style cast syntax is verbose and cumbersome. C++スタイルのキャストの文法は長くて面倒くさいです。
In general, do not use C-style casts. 原則として、C言語スタイルのキャストは使ってはいけません。 Instead, use these C++-style casts when explicit type conversion is necessary. 明示的な型変換を必要とするときは、C言語スタイルのキャストではなく、C++スタイルのキャストを使ってください。
-
Use brace initialization to convert arithmetic types
(e.g.,
int64_t{x}
). 算術型の変換には、波括弧による初期化を使用してください(例:int64_t{x}
)。 This is the safest approach because code will not compile if conversion can result in information loss. この方法は、型変換によって情報が失われるときにコンパイルエラーとなるため、それを防ぐ最も安全な手段です。 The syntax is also concise. また構文も簡潔です。 -
Use
absl::implicit_cast
to safely cast up a type hierarchy, e.g., casting aFoo*
to aSuperclassOfFoo*
or casting aFoo*
to aconst Foo*
. 型階層におけるアップキャスト(基底クラス方向へのキャスト)を安全に行うためにはabsl::implicit_cast
を使ってください。 これにはFoo*
からSuperclassOfFoo*
のようなキャストや、Foo*
からconst Foo*
のようなキャストを含みます。 C++ usually does this automatically but some situations need an explicit up-cast, such as use of the?:
operator. 通常、C++では、この種類のキャストは自動的に行われるのですが、?:
演算子を使うときなど、一部の場合において、明示的なアップキャストを必要とする場合があります。 -
Use
static_cast
as the equivalent of a C-style cast that does value conversion, when you need to explicitly up-cast a pointer from a class to its superclass, or when you need to explicitly cast a pointer from a superclass to a subclass. あるクラスのポインタを基底クラスのポインタに明示的にアップキャストしたいときや、ある基底クラスのポインタを派生クラスのポインタに明示的にダウンキャストしたいときは、値の変換を行うC言語スタイルのキャストと等価な、static_cast
を使用してください。 In this last case, you must be sure your object is actually an instance of the subclass. このうち、ダウンキャストを行う場合については、オブジェクトが実際にその派生クラスのインスタンスであることを確信できていなければなりません。 -
Use
const_cast
to remove theconst
qualifier (see const).const
修飾子(constを参照)を外すためにはconst_cast
を使ってください。 -
Use
reinterpret_cast
to do unsafe conversions of pointer types to and from integer and other pointer types, includingvoid*
. ポインタ型と整数型との間の変換や、型の異なるポインタ型との間の変換(void*
も含みます)のような、安全でない変換を行う場合はreinterpret_cast
を使ってください。 Use this only if you know what you are doing and you understand the aliasing issues. ただし、reinterpret_cast
を使うのは、自分が何をしているのか理解していて、エイリアシングの問題についても十分理解している場合に限定してください。 Also, consider dereferencing the pointer (without a cast) and usingstd::bit_cast
to cast the resulting value. あるいは、ポインタをキャストせずに参照外し(dereference)してから、std::bit_cast
によって値自体をキャストする方法も検討してください。((訳注: C++20からはstd::bit_cast
もあります)) -
Use
std::bit_cast
to interpret the raw bits of a value using a different type of the same size (a type pun), such as interpreting the bits of adouble
asint64_t
.double
のビット列をint64_t
として解釈するような、 生のビット列を同じサイズの違う型に再解釈(type-punning)したいときは、std::bit_cast
を使用してください。
See the
RTTI section for guidance on the use of
dynamic_cast
.
dynamic_cast
に関するガイドについては実行時型情報(RTTI)のセクションも参照してください。
ストリーム
Use streams where appropriate, and stick to "simple"
usages.
適切な場合には、後述するシンプルな使い方に限り、ストリームを使ってもかまいません。
Overload <<
for streaming only for types
representing values, and write only the user-visible value, not any
implementation details.
ストリーム演算子<<
のオーバーロードは、対象の型のオブジェクトが何らか値を表現する場合に限ります。また、その実装を、値を人間が判読可能な形にフォーマットするだけにとどめ、それ以上の詳細な実装を持たないようにしてください。
Streams are the standard I/O abstraction in C++, as
exemplified by the standard header <iostream>
.
ストリームは、標準ヘッダー<iostream>
でも例示される、C++における標準的な入出力の抽象です。
They are widely used in Google code, mostly for debug logging
and test diagnostics.
Googleのコードにおいても、ストリームは、デバッグログ出力やテスト診断結果出力などを目的として広く使われています。
The <<
and >>
stream operators provide an API for formatted I/O that
is easily learned, portable, reusable, and extensible.
C++のストリームは、演算子<<
と>>
によって、学びやすさ、移植性、再利用可能性、拡張性などの面で優れた、フォーマット済み入出力を行うAPIを提供します。
printf
, by contrast, doesn't even support
std::string
, to say nothing of user-defined types,
and is very difficult to use portably.
これとは対照的に、printf
系のAPIでは、ユーザー定義型はおろか、std::string
への対応すらなく、その使い方についても、正しく移植性を保って使うことが非常に難しいです。
printf
also obliges you to choose among the
numerous slightly different versions of that function,
and navigate the dozens of conversion specifiers.
さらに、printf
系のAPIには、微妙に異なるたくさんのバージョンが存在し、その変換指定子も非常にたくさんの種類があります。printf
を使うときは、常に、それらの中から適切なものを選んで、適切に使うことを強制されることになります。
Streams provide first-class support for console I/O
via std::cin
, std::cout
,
std::cerr
, and std::clog
.
ストリームは、std::cin
、std::cout
、std::cerr
、std::clog
を通して、最上級のコンソール入出力をサポートします。
The C APIs do as well, but are hampered by the need to
manually buffer the input.
もちろん、C言語のAPIを用いても同様のことは実現できるのですが、その場合には、入力を手動でバッファする必要があるなど、いくぶん面倒にはなります。
- Stream formatting can be configured by mutating the state of the stream. ストリームによって値がどうフォーマットされるのかに関わる設定は、そのストリーム自体の状態を変更することによって保持されます。 Such mutations are persistent, so the behavior of your code can be affected by the entire previous history of the stream, unless you go out of your way to restore it to a known state every time other code might have touched it. この状態変更はストリームに永続的に残るため、ただストリームに値を書き出すだけでは、それまでのストリーム操作の履歴全体が、その出力形式に影響を及ぼしてしまいます。これを避けるためには、他のコードによってストリームの状態が変更されている可能性のあるすべての箇所で、その都度、ストリームの状態を設定し直す必要があります。 User code can not only modify the built-in state, it can add new state variables and behaviors through a registration system. また、ユーザーのコードは、ストリームの組み込みの状態を変更できるだけでなく、新しい状態変数や挙動を追加することもできてしまいます。
- It is difficult to precisely control stream output, due to the above issues, the way code and data are mixed in streaming code, and the use of operator overloading (which may select a different overload than you expect). このような問題点の他にも、ストリームを使うときには、コードとデータを混ぜて記述する必要があったり、演算子のオーバーロードが意図通りに解決されるよう注意する必要があったりと、その出力を正確にコントロールするのは難しいです。
-
The practice of building up output through chains
of
<<
operators interferes with internationalization, because it bakes word order into the code, and streams' support for localization is flawed. また、ストリームにおいて<<
演算子を連ねて出力を構築する手法は、そのコード上に、出力される語句の順序を固定してしまうため、国際化対応を行う際の障壁となることがあります。 その他、ストリーム自体が持つ多言語対応機能にも欠陥があります。 - The streams API is subtle and complex, so programmers must develop experience with it in order to use it effectively. ストリームのAPIは繊細かつ複雑であり、これらを効率的に使いこなせるようになるためには、ストリームに対するそれなりの開発経験を要します。
-
Resolving the many overloads of
<<
is extremely costly for the compiler. コンパイル時、コンパイラが、たくさんの<<
演算子オーバーロードの中からどれを呼び出すか解決する工程には、非常にコストがかかります。 When used pervasively in a large code base, it can consume as much as 20% of the parsing and semantic analysis time. 演算子のオーバーロードが、大きなコードベースで、あまねく使われたとき、これらの工程は、構文解析と意味解析の時間のうちの20%相当をも消費することがあります。
Use streams only when they are the best tool for the job.
ストリームは、それが目的を果たすための最良のツールであるという場合に限り採用しましょう。
This is typically the case when the I/O is ad-hoc, local,
human-readable, and targeted at other developers rather than
end-users.
ストリームが適している典型例としては、(エンドユーザー向けではなく)他の開発者によって利用されることを想定した、アドホックで、局所的な、人間が判読可能な入出力を行いたい場合が相当します。
Be consistent with the code around you, and with the
codebase as a whole; if there's an established tool for
your problem, use that tool instead.
ただし、周りのコードやコードベース全体との一貫性は保つようにしてください。このような目的を果たす手段として、プロジェクトで既に別の方法が確立されているのであれば、それらに倣ってください。
In particular,
logging libraries are usually a better
choice than std::cerr
or std::clog
for diagnostic output, and the libraries in
absl/strings
or the equivalent are usually a
better choice than std::stringstream
.
特に、診断系の出力を行う用途では、通常、単にstd::cerr
やstd::clog
を使うよりも、ログ出力に特化したライブラリを用いる方が適切です。
また、std::stringstream
についても、それらのかわりにabsl/strings
や同等のライブラリを使う方がよいでしょう。
Avoid using streams for I/O that faces external users or handles untrusted data. 外部ユーザーのための入出力や、信頼できないデータを扱うときは、ストリームを使うのは避けてください。 Instead, find and use the appropriate templating libraries to handle issues like internationalization, localization, and security hardening. これらの用途では、ストリームではなく、多言語化に対応していて、かつ、セキュリティ的にも強固な、何らかの適切なテンプレートエンジンを探し出し、それを使うようにしてください。
If you do use streams, avoid the stateful parts of the
streams API (other than error state), such as imbue()
,
xalloc()
, and register_callback()
.
これらのことを踏まえて、それでもストリームを使うのであれば、その使い方において、エラー状態以外の内部状態を変更するストリームAPI(たとえば、imbue()
やxalloc()
、register_callback()
など)を使うのを避けてください。
Use explicit formatting functions (such as
absl::StreamFormat()
) rather than
stream manipulators or formatting flags to control formatting
details such as number base, precision, or padding.
出力時の基数、精度、出力幅などを変更したいときは、ストリームマニピュレータやフラグなどのかわりに、明示的なフォーマット関数(たとえばabsl::StreamFormat()
)を使ってください。
Overload <<
as a streaming operator
for your type only if your type represents a value, and
<<
writes out a human-readable string
representation of that value.
独自の型に対するストリーム演算子<<
のオーバーロードは、その型のオブジェクトが何らかの値を表す場合に限定し、その演算子<<
の定義において、オブジェクトが表す値を人間が判読可能な文字列表現に書き出す実装を行ってください。
Avoid exposing implementation
details in the output of <<
; if you need to print
object internals for debugging, use named functions instead
(a method named DebugString()
is the most common
convention).
また、演算子<<
によって、クラス内部実装の詳細を公開してしまわないように気をつけてください。
たとえば、デバッグ用途でオブジェクトの内部状態を出力したいときは、演算子<<
ではなく、名前付きの関数を使ってください(この目的では、DebugString()
という名前のメンバ関数を持たせるのが、もっとも一般的な慣習です)。
前置インクリメントと前置デクリメント
Use the prefix form (++i
) of the increment and
decrement operators unless you need postfix semantics.
インクリメント演算子とデクリメント演算子は、後置(i++
)する必要がない限り、前置(++i
)します。
When a variable
is incremented (++i
or i++
) or
decremented (--i
or i--
) and
the value of the expression is not used, one must decide
whether to preincrement (decrement) or postincrement
(decrement).
変数をインクリメント(++i
もしくはi++
)あるいはデクリメント(--i
もしくはi--
)するとき、その式の結果が使われない場合に、演算子を前置するか後置するか決めなければなりません。
A postfix increment/decrement expression evaluates to the value
as it was before it was modified.
演算子を後置する形(i++
)の式は、更新前の値に評価されます。
This can result in code that is more
compact but harder to read.
この評価結果を用いるコードは、コードをコンパクトにできる場合がありますが、読みにくくもなります。
The prefix form is generally more readable, is
never less efficient, and can be more efficient because it doesn't need to
make a copy of the value as it was before the operation.
一方で、演算子を前置する形(++i
)は一般に読みやすく、決して非効率になることもありません。むしろ、操作前の値のコピーをとっておく必要がない分、生成されるコードがより効率的になることもあります。
The tradition developed, in C, of using post-increment, even
when the expression value is not used, especially in
for
loops.
特にC言語での開発において、for
ループなどの式の結果が使われない場合でも、伝統的に、演算子を後置する形(i++
)が使われてきました。
Some find post-increment easier
to read, since the "subject" (i
) precedes
the "verb" (++
), just like in English.
この形は、英文法の語順と同じ、主語(i
)の後に動詞(++
)が続く形をしているため、こちらの方が読みやすいと感じる人もいるでしょう。
Use prefix increment/decrement, unless the code explicitly
needs the result of the postfix increment/decrement expression.
コードにおいて、後置インクリメント(i++
)・後置デクリメント(i--
)の式の結果を明示的に必要とする場合を除き、常に前置インクリメント(++i
)・前置デクリメント(--i
)を使用してください。
constの使い方
In APIs, use const
whenever it makes sense.
APIを定義するとき、理に適っているのならばいつでも常にconst
を使います。
constexpr
is a better choice for some uses of
const.
さらに、const
よりもconstexpr
を用いる方が適切な場合もあります。
Declared variables and parameters can be preceded
by the keyword const
to indicate the variables
are not changed (e.g., const int foo
).
変数宣言や引数宣言では、const int foo
のように、変数名の前にconst
キーワードを前置することで、それらの変数が不変であることを示すことができます。
Class
functions can have the const
qualifier to
indicate the function does not change the state of the
class member variables (e.g., class Foo { int
Bar(char c) const; };
).
また、クラスのメンバ関数の宣言において、class Foo { int Bar(char c) const; };
のように、const
修飾子をつけることで、その関数がクラスのメンバ変数の状態を変更しないことを示すことができます。
Easier for people to understand how variables are being
used.
const
修飾された変数は不変であるため、プログラムにおけるその変数の扱われ方に関して、コードを理解しやすくなります。
Allows the compiler to do better type checking,
and, conceivably, generate better code.
また、コンパイル時において、よりよい型チェックを行うことができ、場合によって、より効率的なコードを生成できることがあります。
Helps people
convince themselves of program correctness because they
know the functions they call are limited in how they can
modify your variables.
関数を呼ぶ際、その引数がconst
であれば、変数が関数によって変更される心配がないため、プログラムの正当性について納得しやすくなります。
Helps people know what functions
are safe to use without locks in multi-threaded
programs.
また、メンバ関数に対するconst
修飾子はマルチスレッドプログラムにおいて、どの関数をロックなしに呼び出せるのか判断するための材料にもなります。
const
is viral: if you pass a
const
variable to a function, that function
must have const
in its prototype (or the
variable will need a const_cast
).
const
は伝染します。
const
変数を関数に渡すときは、関数宣言においても引数がconst
修飾されている必要があり、さもなくば、const_cast
を必要とします。
This can
be a particular problem when calling library
functions.
これは、特に、ライブラリ関数を呼び出すときに問題となることがあります。
We strongly recommend using const
in APIs (i.e., on function parameters, methods, and
non-local variables) wherever it is meaningful and accurate.
APIの定義において (すなわち、関数引数やメソッド、非ローカル変数の宣言において)、意味が通り、かつ、正しいときは、いつでも常にconst
をつけることを強く推奨します。
This
provides consistent, mostly compiler-verified documentation
of what objects an operation can mutate.
これらのconst
修飾子は、対象とする操作がどのオブジェクトを変更しうるのかに関する、一貫した、かつ、コンパイラによって検証済みでもあるドキュメントの役割を果たします。
Having
a consistent and reliable way to distinguish reads from writes
is critical to writing thread-safe code, and is useful in
many other contexts as well.
このような、読み込みと書き込みとを、一貫性と信頼性を持って区別できる手段は、特にスレッドセーフなコードを書くときには必要不可欠ですし、また、それ以外の多くの文脈においても同様に有用ですしょう。
In particular:
特に以下の事項を確認してください。
-
If a function guarantees that it will not modify an argument
passed by reference or by pointer, the corresponding function parameter
should be a reference-to-const (
const T&
) or pointer-to-const (const T*
), respectively. 参照渡しやポインタ渡しの関数引数について、関数がそれらの変数を変更しないことを保証するときは、対応する引数をconst
参照(const T&
)か、const
ポインタ(const T*
)とします。 -
For a function parameter passed by value,
const
has no effect on the caller, thus is not recommended in function declarations. 値渡しの関数引数については、const
をつけても、呼び出し側に何ら影響を与えないため、関数宣言では非推奨とします。 See TotW #109. TotW #109を参照してください。 -
Declare methods to be
const
unless they alter the logical state of the object (or enable the user to modify that state, e.g., by returning a non-const
reference, but that's rare), or they can't safely be invoked concurrently. クラスのメンバ関数は、その関数がオブジェクトの論理的な状態を変更しない (メンバへの非const
参照なども返さない)、かつ、複数のスレッドから同時に呼び出しても安全であるとき、常にconst
修飾子をつけて宣言してください。
Using const
on local variables is neither encouraged
nor discouraged.
ローカル変数にconst
をつけるか否かは、推奨も非推奨もしません。
All of a class's const
operations should be safe
to invoke concurrently with each other.
クラスのすべてのconst
な操作は、それらが互いに同時に呼び出されても安全であるようにしてください。
If that's not feasible, the class must
be clearly documented as "thread-unsafe".
そのようにできない場合は、そのクラスがスレッドセーフではないことについて、明確にドキュメントに記述してください。
constをどこに置くか
Some people favor the form int const *foo
to const int* foo
.
人によっては、const int* foo
の形よりint const *foo
の形を推す人もいます。
They argue that this is
more readable because it's more consistent: it keeps the
rule that const
always follows the object
it's describing.
彼らの主張は「そちらの方が、常にconst
がオブジェクトの後に続くというルールを一貫して保てるため、可読性が高い」というものです。
However, this consistency argument
doesn't apply in codebases with few deeply-nested pointer
expressions since most const
expressions
have only one const
, and it applies to the
underlying value.
しかし、この一貫性に関する主張は、多重ネストされたポインタ式がほとんど出現しないコードベースにおいては、あまり当てはまりません。
ほとんどのconst
の式において、const
は1度だけ表れ、かつ、そのconst
はポインタが指し示す先の値に対して適用されることが多いためです。
In such cases, there's no consistency
to maintain.
このような場合、そもそも、維持すべき一貫性そのものが存在しなくなります。
Putting the const
first is
arguably more readable, since it follows English in
putting the "adjective" (const
) before the
"noun" (int
).
一方で、const
を前置する形(const int
)は、英文法の語順のように、形容詞const
の後に名詞int
が続くため、おそらく、多くの人にとって読みやすいと感じられるはずです。
That said, while we encourage putting
const
first, we do not require it.
以上より、const
を前置する形を推奨としますが、これを必須のルールとはしません。
But be
consistent with the code around you!
ただし、常に周囲のコードとの一貫性は保つようにしてください。
constexpr, constinit, constevalの使い方
Use constexpr
to define true
constants or to ensure constant initialization.
真の定数を宣言したいとき、あるいは、定数初期化を保証したいときには、constexpr
を使います。
Use constinit
to ensure constant
initialization for non-constant variables.
非定数変数に対する定数初期化を保証したいときは、constinit
を使います。
Some variables can be declared constexpr
to indicate the variables are true constants, i.e., fixed at
compilation/link time.
変数宣言においてconstexpr
をつけることで、それらが真の定数(すなわち、コンパイル時かリンク時に確定する不変の値)であることを示すことができます。
Some functions and constructors
can be declared constexpr
which enables them
to be used in defining a constexpr
variable.
関数やコンストラクタの宣言においてconstexpr
をつけると、それらはconstexpr
変数の定義のためにも用いることができるようになります。
Functions can be declared consteval
to restrict their use to compile time.
関数宣言においてconsteval
をつけると、それらの関数をコンパイル時にのみ評価できる(呼び出せる)ように制限することができます。
Use of constexpr
enables definition of
constants with floating-point expressions rather than
just literals; definition of constants of user-defined
types; and definition of constants with function
calls.
constexpr
を用いると、単なるリテラルでない浮動小数点の式による定数や、ユーザー定義の型の定数、あるいは、関数呼び出しを伴う定数を宣言することができます。
Prematurely marking something as constexpr
may cause
migration problems if later on it has to be downgraded.
早まったconstexpr
宣言は、後にダウングレードの必要が生じた際に、作業の妨げとなるかもしれません。
Current restrictions on what is allowed in constexpr
functions and constructors may invite obscure workarounds
in these definitions.
また、現時点のconstexpr
関数・コンストラクタにはできることに制限があるため、それらの制限を迂回する目的だけのために、回りくどいコーディング手法を引き込んでしまうかもしれません。
constexpr
definitions enable a more
robust specification of the constant parts of an
interface.
constexpr
を用いると、インターフェースにおける不変部分の仕様をより堅牢にすることができます。
Use constexpr
to specify true
constants and the functions that support their
definitions.
真の定数や、それらの定義に用いる関数を定義するためには、constexpr
を使ってください。
consteval
may be used for
code that must not be invoked at runtime.
実行時に呼び出されてはならないコードのためには、consteval
を使ってもかまいません。
Avoid complexifying function definitions to
enable their use with constexpr
.
ただし、関数をconstexpr
に対して適格にする目的のためだけにコードを複雑化させることは避けてください。
Do not use
constexpr
or consteval
to force inlining.
また、インライン化を強制する目的ではconstexpr
やconsteval
を使ってはいけません。
整数型
Of the built-in C++ integer types, the only one used
is
int
.
C++の組み込みの整数型の中では、使ってよいのはint
だけです。
If a program needs an integer type of a
different size, use
an exact-width integer type from
<cstdint>
, such as
int16_t
.
プログラムで異なるサイズの整数型を必要とするときは、<cstdint>
から、int16_t
のような、サイズが厳密に定められている整数型を使ってください。
If you have a
value that could ever be greater than or equal to 2^31,
use a 64-bit type such as int64_t
.
値が2³¹以上になりうる変数には、int64_t
のような64ビット型を使ってください。
Keep in mind that even if your value won't ever be too large
for an int
, it may be used in intermediate
calculations which may require a larger type.
また、計算結果の値がint
の範囲内に収まるとわかっていても、その計算過程で一時的に大きな型を必要とする場合がありますので、この点には常に十分注意してください。
When in doubt,
choose a larger type.
迷ったときには、より大きい型を選びましょう。
C++ does not specify exact sizes for the integer types
like int
.
C++の仕様では、int
等の組み込みの整数型のサイズが定めてられていません。
Common sizes on contemporary architectures are
16 bits for short
, 32 bits for int
, 32 or 64
bits for long
, and 64 bits for long long
,
but different platforms make different choices, in particular
for long
.
現代的なアーキテクチャにおいては、short
は16ビット、int
は32ビット、long
は32ビットか64ビット、long long
は64ビットという環境が一般的ですが、これらの整数型のサイズはプラットフォームごとに異なっており、特にlong
には注意が必要です。
Uniformity of declaration. 画一的な宣言を用いることができます。
The sizes of integral types in C++ can vary based on compiler and architecture. C++の組み込み整数型のサイズは、コンパイラとアーキテクチャによって様々に異なります。
The standard library header <cstdint>
defines types
like int16_t
, uint32_t
,
int64_t
, etc.
標準ライブラリヘッダーの<cstdint>
によって、int16_t
やuint32_t
、int64_t
などの型が定義されています。
You should always use
those in preference to short
, unsigned
long long
and the like, when you need a guarantee
on the size of an integer.
整数型のサイズを保証する必要があるときは、short
やunsigned long long
などの組み込み型ではなく、常にサイズの明示された型を使ってください。
Prefer to omit the std::
prefix for these types, as the extra 5 characters do
not merit the added clutter.
また、これらの型については、std::
を省略する方が望ましいです。この余分な5文字は、その乱雑さに見合うメリットがありません。
Of the built-in integer types, only
int
should be used.
組み込みの整数型については、int
のみを使います。
When appropriate, you
are welcome to use standard type aliases like
size_t
and ptrdiff_t
.
また、適切な場合はsize_t
やptrdiff_t
などの標準の型エイリアスを使うことも、もちろん問題ありません。
We use int
very often, for integers we
know are not going to be too big, e.g., loop counters.
int
は非常によく使われます。たとえばループカウンタなど、そこまで大きくならないとわかっている整数については、
Use plain old int
for such things.
普通のint
を使ってください。
You
should assume that an int
is
at least 32 bits, but don't
assume that it has more than 32 bits.
int
は最低でも32ビット以上あると考えてかまいませんが、32ビットよりも大きいと考えてはいけません。
If you need a 64-bit
integer type, use int64_t
or uint64_t
.
64ビットの整数型が必要なときは、int64_t
かuint64_t
を使ってください。
For integers we know can be "big",
use
int64_t
.
整数が大きくなるかもしれないときはint64_t
を使ってください。
You should not use the unsigned integer types such as
uint32_t
, unless there is a valid
reason such as representing a bit pattern rather than a
number, or you need defined overflow modulo 2^N.
unsigned
の整数型(uint32_t
等)は、何らか正当な理由がなければ使ってはいけません。これには、たとえば、変数が数値ではなくビットパターンを表しているときや、あるいは、2^Nでの剰余によるオーバーフロー時の動作を定義したいときなどが当てはまります。
In
particular, do not use unsigned types to say a number
will never be negative.
一方で、特に、整数が決して負にならないことを表明する目的ではunsigned
型を使ってはいけません。
Instead, use
assertions for this.
この用途には、かわりにアサーションを使ってください。
If your code is a container that returns a size, be sure to use a type that will accommodate any possible usage of your container. 何らかのコンテナ型を作るときは、そのサイズを返すための型として、そのコンテナ型がどのような(適正な)使われ方をしたとしても対応可能な型を選んでください。 When in doubt, use a larger type rather than a smaller type. 迷ったときは、より大きな型を選びましょう。
Use care when converting integer types. 整数型の変換には注意してください。 Integer conversions and promotions can cause undefined behavior, leading to security bugs and other problems. 整数型の変換や拡張は、ときに未定義動作を引き起こし、セキュリティ上のバグやその他の問題に繋がることがあります。
unsigned
整数型について
Unsigned integers are good for representing bitfields and modular
arithmetic.
unsigned
整数型は、ビットフィールドや剰余演算に有用です。
Because of historical accident, the C++ standard also uses
unsigned integers to represent the size of containers - many members
of the standards body believe this to be a mistake, but it is
effectively impossible to fix at this point.
歴史的な事情により、C++標準では、コンテナのサイズを表すためにunsigned
整数型が使われており、この点について多くのC++標準化委員会のメンバーがこれを失敗だったと考えていますが、現在において、もはや修正は不可能となってしまいました。
The fact that unsigned
arithmetic doesn't model the behavior of a simple integer, but is
instead defined by the standard to model modular arithmetic (wrapping
around on overflow/underflow), means that a significant class of bugs
cannot be diagnosed by the compiler.
標準によって定義されるunsigned
整数演算は、単純な整数演算の挙動をモデル化したものではなく、オーバーフロー時に値が周回する、合同算術をモデル化したものです。
このことは、これに起因する様々なバグについて、コンパイラが診断できないということを意味します。
In other cases, the defined
behavior impedes optimization.
また、このような挙動の定義のために、コンパイラによる最適化が妨げられることもあります。
That said, mixing signedness of integer types is responsible for an
equally large class of problems.
とはいえ、signed
整数型とunsigned
整数型を混在させてしまうと、それもまた同様に様々なバグの要因となります。
The best advice we can provide: try
to use iterators and containers rather than pointers and sizes, try
not to mix signedness, and try to avoid unsigned types (except for
representing bitfields or modular arithmetic).
ここでの最大限のアドバイスとしては、「コンテナについては、ポインタやサイズではなく、イテレータを使う」「signed
整数型とunsigned
整数型が混在しないようにする」「(ビットフィールドや合同演算を目的としない限り) そもそも、unsigned
整数型を使うのを避ける」というところです。
Do not use an unsigned
type merely to assert that a variable is non-negative.
変数が常に非負であることを主張する目的程度のことでは、unsigned
整数型を使ってはいけません。
浮動小数点型
Of the built-in C++ floating-point types, the only ones used
are float
and
double
.
組み込みのC++の浮動小数点型のうち、float
とdouble
だけを使います。
You may assume that these types represent IEEE-754 binary32
and binary64, respectively.
これらの型は、それぞれ IEEE-754 binary32 と binary64 を表すものと仮定してかまいません。
Do not use long double
, as it gives non-portable
results.
long double
には移植性がないため、これを使用してはいけません。
アーキテクチャ間の移植性
Write architecture-portable code. アーキテクチャ間で移植性のあるコードを書いてください。 Do not rely on CPU features specific to a single processor. 特定のCPUの固有機能に依存してはいけません。
When printing values, use type-safe numeric formatting libraries like
absl::StrCat
,absl::Substitute
,absl::StrFormat
, orstd::ostream
instead of theprintf
family of functions. 値を表示するときは、printf
系の関数ではなく、型安全に数値型のフォーマットを行うことができるライブラリを使用してください。absl::StrCat
,absl::Substitute
,absl::StrFormat
, orstd::ostream
などがあてはまります。When moving structured data into or out of your process, encode it using a serialization library like Protocol Buffers rather than copying the in-memory representation around. 構造体などの構造化されたデータをプロセスの境界を越えて外部とやりとりするときは、メモリ上の表現をそのまま持ち回るのではなく、Protocol Buffersのようなシリアライズライブラリを用いてエンコードしてください。
If you need to work with memory addresses as integers, store them in
uintptr_t
s rather thanuint32_t
s oruint64_t
s. もし、メモリアドレスを整数として扱う必要が生じた場合は、それらをuint32_t
やuint64_t
ではなく、uintptr_t
を使って保持してください。-
Use braced-initialization as needed to create 64-bit constants. For example: 64ビット定数を作るときは、波括弧初期化を使ってください。例を示します。
int64_t my_value{0x123456789}; uint64_t my_mask{uint64_t{3} << 48};
Use portable floating point types; avoid
long double
. 移植性のある浮動小数点型を使用してください。long double
は避けてください。Use portable integer types; avoid
short
,long
, andlong long
. 移植性のある整数型を使用してください。short
、long
、long long
は避けてください。
プリプロセッサマクロ
Avoid defining macros, especially in headers; prefer
inline functions, enums, and const
variables.
マクロを定義しないでください。
特にヘッダーではマクロを定義しないでください。
かわりに、インライン関数や、列挙型、const
変数を用いましょう。
Name macros with a project-specific prefix.
マクロの名前にはプロジェクト固有の接頭辞(接頭辞)をつけます。
Do not use
macros to define pieces of a C++ API.
C++ APIの定義としてマクロを使ってはいけません。
Macros mean that the code you see is not the same as the code the compiler sees. マクロを使うということは、あなたが見ているコードと、コンパイラが見ているコードとが同じではなくなるということを意味します。 This can introduce unexpected behavior, especially since macros have global scope. 特にマクロはグローバルスコープを持つため、それによって、予期せぬ挙動を招くことがあります。
The problems introduced by macros are especially severe when they are used to define pieces of a C++ API, and still more so for public APIs. マクロによって引き起こされる問題は、特にマクロがC++のAPIの一部として定義されたとき、加えて、それらが公開APIであるときは殊更、深刻なものとなる場合があります。 Every error message from the compiler when developers incorrectly use that interface now must explain how the macros formed the interface. 開発者がインターフェースを誤って使用したときに生成されるコンパイルエラーメッセージにおいて、それらのマクロがインターフェースをどのように形成しているのか説明していなければなりません。 Refactoring and analysis tools have a dramatically harder time updating the interface. また、マクロを使うと、リファクタリングツールや解析ツールがインターフェースを更新するときに、劇的な時間が掛かるようにもなります。 As a consequence, we specifically disallow using macros in this way. 結論として、この用途でのマクロは明確に禁止とします。 For example, avoid patterns like: 例として、次のようなパターンは避けてください。
class WOMBAT_TYPE(Foo) { // ... public: EXPAND_PUBLIC_WOMBAT_API(Foo) EXPAND_WOMBAT_COMPARISONS(Foo, ==, <) };
Luckily, macros are not nearly as necessary in C++ as
they are in C.
幸いなことに、C++ではC言語に比べてほとんどマクロを必要としません。
Instead of using a macro to inline
performance-critical code, use an inline function.
パフォーマンスクリティカルなコードをインライン化する目的では、マクロではなく、インライン関数を使うことができます。
Instead of using a macro to store a constant, use a
const
variable.
定数を保持するためには、マクロではなく、const
変数を使うことができます。
Instead of using a macro to
"abbreviate" a long variable name, use a reference.
長い名前の変数への省略形を作るためには、マクロではなく、参照を使うことができます。
Instead of using a macro to conditionally compile code
... well, don't do that at all (except, of course, for
the #define
guards to prevent double
inclusion of header files).
条件によってコンパイルされるコードを変えるためには、マクロではなく……いや、そもそも、そのようなことはしないでください(もちろん、ヘッダファイルにおけるインクルードガードは例外です)。
It makes testing much more
difficult.
そのようなことをすると、テストが非常に難しくなります。
Macros can do things these other techniques cannot, and you do see them in the codebase, especially in the lower-level libraries. マクロを使うと、他の手段では不可能なことが実現できます。 コードベースの中でも、特に低レベルライブラリにおいて、それらを見かけることがあるかもしれません。 And some of their special features (like stringifying, concatenation, and so forth) are not available through the language proper. たとえば、マクロには、シンボルの文字列化や連結といった特殊な機能があり、これらは言語系では提供されていません。 But before using a macro, consider carefully whether there's a non-macro way to achieve the same result. それでも、マクロを使う前に、それ以外の方法で同様の結果を得ることができないか、よく検討するようにしてください。 If you need to use a macro to define an interface, contact your project leads to request a waiver of this rule. どうしてもマクロを使ってインターフェースを定義しなければならないときには、このルールの放棄について、プロジェクトリーダーと相談してください。
The following usage pattern will avoid many problems with macros; if you use macros, follow it whenever possible: 以下に示すパターンに従うと、マクロにまつわる多くの問題を避けることができます。もし、マクロを使うのであれば、可能な限りこれらに従ってください。
-
Don't define macros in a
.h
file..h
ファイル内ではマクロを定義してはいけません。 -
#define
macros right before you use them, and#undef
them right after. マクロは、使う直前に#define
し、使い終わったらすぐに#undef
してください。 -
Do not just
#undef
an existing macro before replacing it with your own; instead, pick a name that's likely to be unique. 既存のマクロを自分のものに置き換える目的で#undef
してはいけません。かわりに、新しいマクロに別のユニークな名前をつけてください。 - Try not to use macros that expand to unbalanced C++ constructs, or at least document that behavior well. アンバランスなC++の構造に展開されるマクロを使うのは避けてください。 あるいは、少なくとも、その挙動について十分にドキュメントを記述してください。
-
Prefer not using
##
to generate function/class/variable names. 関数名やクラス名、あるいは変数名を生成するために##
を使うのはやめましょう。
Exporting macros from headers (i.e., defining them in a header
without #undef
ing them before the end of the header)
is extremely strongly discouraged.
ヘッダーからマクロをエクスポートすること(すなわち、ヘッダーでマクロを定義し、そのまま#undef
しないこと)は、まったく非常に強く非推奨です。
If you do export a macro from a
header, it must have a globally unique name.
どうしても、ヘッダーからマクロをエクスポートする場合は、必ずグローバルにユニークな名前をつけてください。
To achieve this, it
must be named with a prefix consisting of your project's namespace
name (but upper case).
このことを達成するために、マクロの名前には、そのプロジェクトの名前空間の名前をすべて大文字にして接頭辞としてつけるようにしてください。
0
とnullptr
とNULL
Use nullptr
for pointers, and '\0'
for chars (and
not the 0
literal).
ポインタにはnullptr
、文字には'\0'
を使います。これらの目的でリテラルの0
は使いません。
For pointers (address values), use nullptr
, as this
provides type-safety.
ポインタ(アドレス値)には、nullptr
を使ってください。nullptr
を使えば型安全になります。
Use '\0'
for the null character.
ヌル文字には'\0'
を使ってください。
Using the correct type makes
the code more readable.
正しい型のリテラルを用いることで、コードがより読みやすくなります。
sizeof
Prefer sizeof(varname)
to
sizeof(type)
.
sizeof(型)
よりもsizeof(変数名)
の形を優先的に使いましょう。
Use sizeof(varname)
when you
take the size of a particular variable.
具体的な変数のサイズを知りたいときにはsizeof(変数名)
とします。
sizeof(varname)
will update
appropriately if someone changes the variable type either
now or later.
sizeof(変数名)
の形を使っていれば、将来、変数の型が変更される場合にも、適切に追従することができます。
You may use
sizeof(type)
for code unrelated
to any particular variable, such as code that manages an
external or internal data format where a variable of an
appropriate C++ type is not convenient.
一方で、コードが具体的な変数に結びつかないときにはsizeof(型)
の形を使ってもかまいません。
たとえば、以下の例のように、外部または内部のデータフォーマットそのものを扱っていて、わざわざ適切なC++の型の変数を用意するのでは不便な場合です。
MyStruct data; memset(&data, 0, sizeof(data));
memset(&data, 0, sizeof(MyStruct));
memset(&data, 0, sizeof(MyStruct)); // これは避ける
if (raw_size < sizeof(int)) { LOG(ERROR) << "compressed record not big enough for count: " << raw_size; return false; }
型推論(auto
を含む)
Use type deduction only if it makes the code clearer to readers who aren't familiar with the project, or if it makes the code safer. 型推論は、型推論によって、そのプロジェクトに馴染みのないコード読者にとっても、コードが読みやすくなることを期待できる場合や、あるいは、型推論によってコードをより安全にできる場合に限って使用します。 Do not use it merely to avoid the inconvenience of writing an explicit type. 明示的に型を記述するのが面倒だからという理由では、型推論を使ってはいけません。
There are several contexts in which C++ allows (or even requires) types to be deduced by the compiler, rather than spelled out explicitly in the code: C++では、いくつかの文脈において、型を明示するかわりに、コンパイラによる推論を許す(あるいは、そうしなければならない)ことがあります。
- Function template argument deduction 関数テンプレートの実引数推論
-
A function template can be invoked without explicit template arguments.
関数テンプレートは、明示的にテンプレート引数を与えずとも呼び出すことができます。
The compiler deduces those arguments from the types of the function
arguments:
コンパイラは、実際に関数に渡された引数の型から、これらのテンプレート引数を推論します。
template <typename T> void f(T t); f(0); // Invokes f<int>(0)
template <typename T> void f(T t); f(0); // f<int>(0) を呼び出す
-
auto
variable declarationsauto
変数宣言 -
A variable declaration can use the
auto
keyword in place of the type. 変数宣言では、型のかわりにauto
キーワードを使うことができます。 The compiler deduces the type from the variable's initializer, following the same rules as function template argument deduction with the same initializer (so long as you don't use curly braces instead of parentheses). コンパイラは、変数の初期化子から、関数テンプレートの実引数推論と同じ規則に従い、変数の型を推論します(丸括弧のかわりに波括弧を使用しない限り)。auto a = 42; // a is an int auto& b = a; // b is an int& auto c = b; // c is an int auto d{42}; // d is an int, not a std::initializer_list<int>
auto a = 42; // a は int型 auto& b = a; // b は int&型 auto c = b; // c は int型 auto d{42}; // d は int型 ※ std::initializer_list<int>ではない
auto
can be qualified withconst
, and can be used as part of a pointer or reference type, and (since C++17) as a non-type template argument.auto
はconst
で修飾することができ、また、ポインタまたは参照型の一部分として用いることもできます。またC++17からは、非型テンプレート実引数に用いることもできます。 A rare variant of this syntax usesdecltype(auto)
instead ofauto
, in which case the deduced type is the result of applyingdecltype
to the initializer. また、この文法のまれに見る変種として、auto
のかわりにdecltype(auto)
というものもあります。decltype(auto)
を使った場合は、初期化子をdecltype
に渡したときに行われる型推論と同じ規則に従って、その結果の型に解決されることになります。 - Function return type deduction 関数戻り値の型推論
-
auto
(anddecltype(auto)
) can also be used in place of a function return type.auto
(とdecltype(auto)
)は、関数の戻り値の型として用いることもできます。 The compiler deduces the return type from thereturn
statements in the function body, following the same rules as for variable declarations: コンパイラは、関数本体のreturn
文から、変数宣言のときと同じ規則に従って、戻り値の型を推論します。auto f() { return 0; } // The return type of f is int
auto f() { return 0; } // f の戻り値の型は int
auto
. また、ラムダ式の戻り値の型も同じ方法で推論させることができますが、ラムダ式の場合は、明示的なauto
ではなく、単に戻り値の型を省略したときに型推論が行われます。 Confusingly, trailing return type syntax for functions also usesauto
in the return-type position, but that doesn't rely on type deduction; it's just an alternate syntax for an explicit return type. なお、紛らわしいことに、関数宣言の末尾に戻り値の型を書く構文においてもauto
キーワードが表れますが、このauto
は型推論とは関係がありません。この構文は、戻り値の型が明示された別の形の構文にすぎず、型推論は行われません。 - Generic lambdas ジェネリックラムダ
-
A lambda expression can use the
auto
keyword in place of one or more of its parameter types. ラムダ式では、仮引数の型としてauto
キーワードを用いることができます。 This causes the lambda's call operator to be a function template instead of an ordinary function, with a separate template parameter for eachauto
function parameter: 1つ以上のauto
仮引数を宣言すると、その関数呼び出し演算子(operator()
)が通常の関数ではなく関数テンプレートとなり、 各auto
仮引数ごとに個別のテンプレート引数が割り当てられます。// Sort `vec` in decreasing order std::sort(vec.begin(), vec.end(), [](auto lhs, auto rhs) { return lhs > rhs; });
// `vec` を降順ソート std::sort(vec.begin(), vec.end(), [](auto lhs, auto rhs) { return lhs > rhs; });
- Lambda init captures ラムダの初期化キャプチャ
-
Lambda captures can have explicit initializers, which can be used to
declare wholly new variables rather than only capturing existing ones:
ラムダのキャプチャでは、既存変数をキャプチャできるだけではなく、明示的な初期化子を与えることで、まったく新しい変数を宣言することもできます。
[x = 42, y = "foo"] { ... } // x is an int, and y is a const char*
[x = 42, y = "foo"] { ... } // x は int で、y は const char*
auto
variables. この構文において、変数の型を明示的に指定する方法はありません。かわりにauto
変数宣言時のルールに従って型推論が行われます。 - Class template argument deduction クラス テンプレートの引数推論
- See below. これについては、後述します。
- Structured bindings 構造化束縛
-
When declaring a tuple, struct, or array using
auto
, you can specify names for the individual elements instead of a name for the whole object; these names are called "structured bindings", and the whole declaration is called a "structured binding declaration".auto
を使ってタプルや構造体や配列を宣言するとき、オブジェクト全体に名前を付けるかわりに、個々の要素に名前を付けることもできます。 これらの名前のことを「構造化束縛(structured binding)」と呼び、その宣言全体を「構造化束縛宣言(structured binding declaration)」と呼びます。 This syntax provides no way of specifying the type of either the enclosing object or the individual names: この構文においては、全体を囲むオブジェクトの型も、個々の要素の型も、いずれも明示的に指定する方法はありません。auto [iter, success] = my_map.insert({key, value}); if (!success) { iter->second = value; }
Theauto
can also be qualified withconst
,&
, and&&
, but note that these qualifiers technically apply to the anonymous tuple/struct/array, rather than the individual bindings. このauto
は、const
、&
、および&&
で修飾することもできます。ただし、これらの修飾子は、技術的には、個々の要素に対してではなく、その宣言で作られる無名のタプルや構造体、配列そのものに適用されていることに注意してください。 The rules that determine the types of the bindings are quite complex; the results tend to be unsurprising, except that the binding types typically won't be references even if the declaration declares a reference (but they will usually behave like references anyway). これらの束縛変数の型を決定するルールはかなり複雑です。 結果的には、概ね想像通りの挙動にはなるのですが、構造化束縛宣言全体が参照である場合であっても、各々の束縛変数の型が参照型になるわけではないため、この点には注意が必要です(ただし、いずれにせよ、それらは参照であるかのように振る舞います)。
(These summaries omit many details and caveats; see the links for further information.) (ここまでの要約においては、多くの詳細と注釈すべき事項が割愛されています。より詳しい知識を得るべく、各リンク先も参照するようにしてください)
- C++ type names can be long and cumbersome, especially when they involve templates or namespaces. C++の型名は、長く、扱いにくいものになることがあります。特にテンプレートや名前空間が絡んだときには顕著です。
- When a C++ type name is repeated within a single declaration or a small code region, the repetition may not be aiding readability. C++の型名は、1つの宣言や小さなコードブロックの中で何度も繰り返されることがあります。しかし、そのような繰り返しが可読性に貢献することはありません。
- It is sometimes safer to let the type be deduced, since that avoids the possibility of unintended copies or type conversions. 意図しないコピーや型変換の可能性を避けるために、むしろ型を推論させる方が安全な場合があります。
C++ code is usually clearer when types are explicit, especially when type deduction would depend on information from distant parts of the code. C++のコードは、通常は、型が明示されている方が明確でわかりやすいものになります。 特に、型推論のために必要な情報が、コードの離れた場所に位置していると、コードを理解しにくくなることがあります。 In expressions like: 次のような例を考えてみましょう。
auto foo = x.add_foo(); auto i = y.Find(key);
it may not be obvious what the resulting types are if the type
of y
isn't very well known, or if y
was
declared many lines earlier.
このコードにおいて、y
の型が十分有名でなかったり、あるいは、y
が何十行も前に宣言されていたりすると、結果の型が何であるかは明確でなくなります。
Programmers have to understand when type deduction will or won't produce a reference type, or they'll get copies when they didn't mean to. また、プログラマーは、型推論によってどの場合に参照型が生成され、どの場合に通常の型が生成されるのか、しっかり理解していなければなりません。さもなくば、意図せずオブジェクトをコピーしてしまうことになるでしょう。
If a deduced type is used as part of an interface, then a programmer might change its type while only intending to change its value, leading to a more radical API change than intended. 型推論がインターフェースの一部として使われてしまっている場合、プログラマーはその値だけを変えるつもりで、うっかり、型をも変えてしまうかもしれません。これによって、本来意図したよりも大きなAPIの変更を招いてしまうかもしれません。
The fundamental rule is: use type deduction only to make the code
clearer or safer, and do not use it merely to avoid the
inconvenience of writing an explicit type.
基本的なルールとして、型推論は、型推論によってコードが明確になる、もしくは、安全になるときに限って使用するようにしてください。型を明示的に記述するのが面倒だ、という理由で型推論を使ってはいけません。
When judging whether the
code is clearer, keep in mind that your readers are not necessarily
on your team, or familiar with your project, so types that you and
your reviewer experience as unnecessary clutter will very often
provide useful information to others.
コードが明確であるかどうかの判定基準を考えるときには、そのコードの読者として、プロジェクトチーム外のプロジェクトに詳しくない人を想定してください。
コードの著者やレビュアーにとって無用に散らかっているように感じられる細かい型の記述であっても、それ以外の人たちにとっては有益な情報源の役割を果たしているということは、実際によくあることです。
For example, you can assume that
the return type of make_unique<Foo>()
is obvious,
but the return type of MyWidgetFactory()
probably isn't.
たとえば、std::make_unique<Foo>()
の戻り値の型は誰が見ても明確であると言えますが、MyWidgetFactory()
の戻り値の型は、おそらくそうではありません。
These principles apply to all forms of type deduction, but the details vary, as described in the following sections. これらの原則は、すべての形の型推論に当てはまります。ただし、細かいところで異なる点もあるため、それらについて以降のセクションで詳しく説明します。
関数テンプレートの引数推論
Function template argument deduction is almost always OK. 関数テンプレート引数の型推論は、ほとんどの場合において問題ありません。 Type deduction is the expected default way of interacting with function templates, because it allows function templates to act like infinite sets of ordinary function overloads. 関数テンプレートにおいては、型推論を用いることがむしろ通常の使用方法として想定されており、それによって、関数テンプレートが無限のオーバーロードを持つ関数集合であるかのように振る舞えます。 Consequently, function templates are almost always designed so that template argument deduction is clear and safe, or doesn't compile. したがって、関数テンプレートは、ほとんど常に、そのテンプレート引数を明確かつ安全に推論できるか、さもなくばコンパイルエラーになるように設計されています。
ローカル変数の型推論
For local variables, you can use type deduction to make the code clearer by eliminating type information that is obvious or irrelevant, so that the reader can focus on the meaningful parts of the code: ローカル変数については、型推論によって、明白あるいは不適切な型に関する情報を省き、コードをより明確にできる場合があります。 そうすることで、コード読者がコードの本質的な部分に集中できるようになります。次の2つの例を見比べてみてください。
std::unique_ptr<WidgetWithBellsAndWhistles> widget = std::make_unique<WidgetWithBellsAndWhistles>(arg1, arg2); absl::flat_hash_map<std::string, std::unique_ptr<WidgetWithBellsAndWhistles>>::const_iterator it = my_map_.find(key); std::array<int, 6> numbers = {4, 8, 15, 16, 23, 42};
auto widget = std::make_unique<WidgetWithBellsAndWhistles>(arg1, arg2); auto it = my_map_.find(key); std::array numbers = {4, 8, 15, 16, 23, 42};
Types sometimes contain a mixture of useful information and boilerplate,
such as it
in the example above: it's obvious that the
type is an iterator, and in many contexts the container type and even the
key type aren't relevant, but the type of the values is probably useful.
型には、上述の例におけるit
のように、有益な情報とただの定型コードとの両方が混在していることがあります。
it
がイテレータ型であることは明白ですし、多くの文脈においてはコンテナの型やキーの型さえも不要な情報でしょう。それでも、値の型に関する情報は依然として有益である場合があります。
In such situations, it's often possible to define local variables with
explicit types that convey the relevant information:
このような状況では、大抵の場合、以下の例のように、型を明示したローカル変数を定義することで、関連する情報を伝えることができます。
if (auto it = my_map_.find(key); it != my_map_.end()) { WidgetWithBellsAndWhistles& widget = *it->second; // Do stuff with `widget` }
if (auto it = my_map_.find(key); it != my_map_.end()) { WidgetWithBellsAndWhistles& widget = *it->second; // `widget`に関する操作を行う }
If the type is a template instance, and the parameters are boilerplate but the template itself is informative, you can use class template argument deduction to suppress the boilerplate. 型がテンプレートインスタンスであって、そのテンプレート引数はただの定型文であるが、それでもテンプレート自体の名前には有益な情報を含む、という場合には、クラステンプレートの実引数推論を利用することで、定型の部分を省略することができる場合があります。 However, cases where this actually provides a meaningful benefit are quite rare. ただし、これらが実際に意味のある利益をもたらす機会はほとんどありません。 Note that class template argument deduction is also subject to a separate style rule. なお、クラステンプレートの実引数推論を行う場合は、それ専用のスタイルルールにも従う必要がありますので、意してください。
Do not use decltype(auto)
if a simpler option will work,
because it's a fairly obscure feature, so it has a high cost in code
clarity.
decltype(auto)
について、より単純な手段で十分な場合には、わざわざdecltype(auto)
を使ってはいけません。decltype(auto)
は、かなり難しい機能であるため、コードの明確さの面で大きなコストとなってしまうためです。
戻り値の型推論
Use return type deduction (for both functions and lambdas) only if the
function body has a very small number of return
statements,
and very little other code, because otherwise the reader may not be able
to tell at a glance what the return type is.
戻り値の型推論を使用するのは、通常の関数とラムダ式のいずれの場合でも、関数本体に含まれるreturn
文の数が非常に少なく、かつ、その他のコードがほとんどない場合に限定してください。コードの読者が、その関数を一目見ただけで戻り値の型を判断できないような場合には、戻り値の型推論を使わないでください。
Furthermore, use it only
if the function or lambda has a very narrow scope, because functions with
deduced return types don't define abstraction boundaries: the implementation
is the interface.
さらに言えば、戻り値の型を推論に委ねる関数は、抽象化の境界を定義せず、その実装そのものがそのままインターフェースとなるため、それらの関数やラムダが非常に狭いスコープから使われる場合のみに限定される方が望ましいです。
In particular, public functions in header files
should almost never have deduced return types.
特に、ヘッダーファイルにて公開される関数では、戻り値の型の推論は、ほとんど、まったく、用いるべきではありません。
引数の型推論
auto
parameter types for lambdas should be used with caution,
because the actual type is determined by the code that calls the lambda,
rather than by the definition of the lambda.
ラムダ式の引数をauto
にするときは注意が必要です。
これは、引数の実際の型が、ラムダの定義ではなく、ラムダを呼び出すコードによって決定されるためです。
Consequently, an explicit
type will almost always be clearer unless the lambda is explicitly called
very close to where it's defined (so that the reader can easily see both),
or the lambda is passed to an interface so well-known that it's
obvious what arguments it will eventually be called with (e.g.,
the std::sort
example above).
したがって、ラムダの定義とその呼び出しがすぐ近くにあって両者のコードを容易に見つけられる場合や、あるいは、ラムダが非常によく知られたインターフェース(std::sort
など)に渡されていて、呼び出し時の実引数の型を容易に想像できる場合などを除けば、それ以外は、ほとんど常に、ラムダの引数の型を明示する方がコードがわかりやすくなります。
ラムダ式の初期化キャプチャ
Init captures are covered by a more specific style rule, which largely supersedes the general rules for type deduction. 初期化キャプチャに関しては、より具体的なスタイルルールによってカバーします。これは、型推論の一般的なルールに大きく取って代わるものです。
構造化束縛
Unlike other forms of type deduction, structured bindings can actually
give the reader additional information, by giving meaningful names to the
elements of a larger object.
他の形式の型推論とは異なり、構造化束縛では、大きなオブジェクトの各要素に意味のある名前を付けられるため、コード読者に付加的な情報を提供することができます。
This means that a structured binding declaration
may provide a net readability improvement over an explicit type, even in cases
where auto
would not.
これは、そのauto
自体が可読性の向上に貢献しないとしても、型を明示的に書くよりも構造化束縛宣言を用いる方が、全体としての可読性が高まることがある、ということを意味します。
Structured bindings are especially
beneficial when the object is a pair or tuple (as in the insert
example above), because they don't have meaningful field names to begin with,
but note that you generally shouldn't use
pairs or tuples unless a pre-existing API like insert
forces you to.
構造化束縛が特に真価を発揮するのは、(上記例のinsert
のように、)、ペアやタプルのような、フィールド名に意味を持たない型を扱う場合です。
(ただし、insert
などの既存のAPIによって強制される場合以外では、そもそもペアやタプルを使うべきではありません。)
If the object being bound is a struct, it may sometimes be helpful to provide names that are more specific to your usage, but keep in mind that this may also mean the names are less recognizable to your reader than the field names. 束縛対象のオブジェクトが構造体である場合、その場での各フィールドの使い方に特化した名前を付け直すことで、コードがわかりやすくなる場合もあります。 ただし、逆に、新しい名前によって元のフィールド名を見分けにくくなってしまうかもしれないことにも気をつけてください。 We recommend using a comment to indicate the name of the underlying field, if it doesn't match the name of the binding, using the same syntax as for function parameter comments: 束縛名と元のフィールド名とを変える場合は、関数引数のコメントと同じ構文で、元のフィールド名をコメントとして記述することをおすすめします。
auto [/*field_name1=*/bound_name1, /*field_name2=*/bound_name2] = ...
As with function parameter comments, this can enable tools to detect if you get the order of the fields wrong. こうすることで、フィールドの順序が間違っていないかどうかを、関数引数のコメントと同様に、ツールに検出させることができるようになるでしょう。
クラステンプレートのテンプレート引数推論(CTAD)
Use class template argument deduction only with templates that have explicitly opted into supporting it. 「クラステンプレートのテンプレート引数推論」は、それについて対応していることが明示されているテンプレートに対してのみ利用します。
Class template argument deduction (often abbreviated "CTAD") occurs when a variable is declared with a type that names a template, and the template argument list is not provided (not even empty angle brackets): クラステンプレートのテンプレート引数推論(しばしば "CTAD" と略される)は、 変数の型がテンプレート名で宣言され、かつ、その宣言でテンプレートの引数リストが与えられなかった場合(空の山括弧もない)に行われます。
std::array a = {1, 2, 3}; // `a` is a std::array<int, 3>
std::array a = {1, 2, 3}; // `a` は std::array<int, 3>
The compiler deduces the arguments from the initializer using the template's "deduction guides", which can be explicit or implicit. コンパイラは、そのテンプレートの明示的または暗黙的な「推論ガイド」を使用して、初期化子からテンプレート引数を推論します。
Explicit deduction guides look like function declarations with trailing
return types, except that there's no leading auto
, and the
function name is the name of the template.
明示的な推論ガイドの見た目は、戻り値の型を末尾におく関数宣言のような形をしていますが、先頭にauto
が置かれず、関数名の部分にテンプレート名が使われます。
For example, the above example
relies on this deduction guide for std::array
:
たとえば、上記の例は、std::array
の次の推論ガイドに依存しています。
namespace std { template <class T, class...U> array(T, U...) -> std::array<T, 1 + sizeof...(U)>; }
Constructors in a primary template (as opposed to a template specialization) also implicitly define deduction guides. テンプレートの特殊化とは異なり、プライマリテンプレートのコンストラクタも、暗黙的な推論ガイドを定義します。
When you declare a variable that relies on CTAD, the compiler selects a deduction guide using the rules of constructor overload resolution, and that guide's return type becomes the type of the variable. CTADを用いた変数宣言においては、コンストラクタのオーバーロード解決の規則に沿って推論ガイドが選択され、そのガイドの戻り値の型が変数の型になります。
CTAD can sometimes allow you to omit boilerplate from your code. CTADでは、コードから定型文を省略できる場合があります。
The implicit deduction guides that are generated from constructors may have undesirable behavior, or be outright incorrect. コンストラクタから自動生成される暗黙的な推論ガイドは、望ましくない動作をしたり、あるいは、まったくもって正しくない場合があります。 This is particularly problematic for constructors written before CTAD was introduced in C++17, because the authors of those constructors had no way of knowing about (much less fixing) any problems that their constructors would cause for CTAD. これらは、特に、C++17でCTADが導入される前に書かれたコンストラクタで問題になりがちです。 これらのコンストラクタの著者は、これらのコンストラクタがCTADによって引き起こす問題について、何ら知る由もなかったでしょう。 Furthermore, adding explicit deduction guides to fix those problems might break any existing code that relies on the implicit deduction guides. さらに、これらの問題を修正するために明示的な推論ガイドを追加しようとすると、今度は、それまでの暗黙的な推論ガイドに依存している既存のコードを壊してしまう恐れさえあります。
CTAD also suffers from many of the same drawbacks as auto
,
because they are both mechanisms for deducing all or part of a variable's
type from its initializer.
CTADにもauto
と同様の欠点が数多くあります。
これらの仕組みは、どちらも、その初期化子から変数の型またはその一部を推論するためのものだからです。
CTAD does give the reader more information
than auto
, but it also doesn't give the reader an obvious
cue that information has been omitted.
CTADは、auto
に比べればより多くの情報を読者に提供していますが、それでも、情報が省略されていること自体を明確に伝えることはありません。
Do not use CTAD with a given template unless the template's maintainers
have opted into supporting use of CTAD by providing at least one explicit
deduction guide (all templates in the std
namespace are
also presumed to have opted in).
テンプレートのメンテナンス担当者によって1つ以上の明示的な推論ガイドが提供されている場合、そのテンプレートは明示的にCTADに対応していると見なすことができます。CTADは、この場合に限り利用するものとし、それ以外の場合はCTADを避けてください (なお、std
名前空間のテンプレートについては、それらのすべてがCTADに対応していると見なしてかまいません)。
This should be enforced with a compiler
warning if available.
これついて、可能であればコンパイラの警告を有効にすることで、強制されるようにしてください。
Uses of CTAD must also follow the general rules on Type deduction. なお、CTADは型推論に関する一般的なルールにも従う必要がありますので、注意してください。
指示付き初期化子
Use designated initializers only in their C++20-compliant form. 指示付き初期化子は、C++20標準に準拠する形式でのみ使用します。
Designated initializers are a syntax that allows for initializing an aggregate ("plain old struct") by naming its fields explicitly: 指示付き初期化子は、各フィールド名を明示しながら集約型(aggregate) (あるいは、POD: Plain Old Structとも) を初期化できる構文です。
struct Point { float x = 0.0; float y = 0.0; float z = 0.0; }; Point p = { .x = 1.0, .y = 2.0, // z will be 0.0 };
The explicitly listed fields will be initialized as specified, and others
will be initialized in the same way they would be in a traditional aggregate
initialization expression like Point{1.0, 2.0}
.
明示的にリストアップされたフィールドは指定のとおりに初期化され、それ以外のフィールドは従来の集約初期化式(Point{1.0, 2.0}
)と同様に初期化されます。
Designated initializers can make for convenient and highly readable
aggregate expressions, especially for structs with less straightforward
ordering of fields than the Point
example above.
指示付き初期化子は、特に上記の Point
の例よりもフィールドの順序付けが複雑な構造体において、便利で非常に読みやすい集約式(aggregate expression)を作成できます。
While designated initializers have long been part of the C standard and supported by C++ compilers as an extension, they were not supported by C++ prior to C++20. 指示付き初期化子の機能は、長い間、標準C言語の一部であり、各C++コンパイラによる拡張機能としてもサポートされていましたが、C++20よりも前のC++ではサポートされていませんでした。
The rules in the C++ standard are stricter than in C and compiler extensions,
requiring that the designated initializers appear in the same order as the
fields appear in the struct definition.
C++標準における規則は、標準C言語やコンパイラの拡張機能のものよりも厳しく、指示付き初期化子が、構造体の定義におけるフィールドと同じ順序で現れることを要求しています。
So in the example above, it is legal
according to C++20 to initialize x
and then z
, but not
y
and then x
.
つまり、前述の例において、C++20に従うと、x
のあとにz
を初期化することは妥当ですが、y
のあとにx
を初期化することはできません。
Use designated initializers only in the form that is compatible with the C++20 standard: with initializers in the same order as the corresponding fields appear in the struct definition. 指示付き初期化子は、C++20標準と互換性のある形式でのみ利用してください。 すなわち、対応するフィールドが構造体定義に現れるのと同じ順序で初期化子を記述するようにしてください。
ラムダ式
Use lambda expressions where appropriate. ラムダ式は適切に使います。 Prefer explicit captures when the lambda will escape the current scope. ラムダが現在のスコープから外に出て行くときは、変数キャプチャを明示的に行いましょう。
Lambda expressions are a concise way of creating anonymous function objects. ラムダ式は匿名関数オブジェクトを作るための簡潔な手段です。 They're often useful when passing functions as arguments. For example: これらは関数の引数として渡す場合にも有用です。
std::sort(v.begin(), v.end(), [](int x, int y) { return Weight(x) < Weight(y); });
They further allow capturing variables from the enclosing scope either explicitly by name, or implicitly using a default capture. さらに、ラムダ式では自身を囲むスコープから変数をキャプチャすることができます。 変数のキャプチャは、明示的に名前を指定するか、暗黙的なデフォルトのキャプチャを使うかのいずれかによって行われます。 Explicit captures require each variable to be listed, as either a value or reference capture: 明示的なキャプチャを行う場合は、次のように、各変数を値キャプチャとするか参照キャプチャとするかを指定しながらリストに並べます。
int weight = 3; int sum = 0; // Captures `weight` by value and `sum` by reference. std::for_each(v.begin(), v.end(), [weight, &sum](int x) { sum += weight * x; });
int weight = 3; int sum = 0; // weigthは値として、sumは参照としてキャプチャします。 std::for_each(v.begin(), v.end(), [weight, &sum](int x) { sum += weight * x; });
Default captures implicitly capture any variable referenced in the
lambda body, including this
if any members are used:
デフォルトキャプチャは暗黙的にラムダの本体で参照されたすべての変数をキャプチャします。何らかのメンバ変数やメンバ関数などが使われている場合はthis
もキャプチャされます。
const std::vector<int> lookup_table = ...; std::vector<int> indices = ...; // Captures `lookup_table` by reference, sorts `indices` by the value // of the associated element in `lookup_table`. std::sort(indices.begin(), indices.end(), [&](int a, int b) { return lookup_table[a] < lookup_table[b]; });
const std::vector<int> lookup_table = ...; std::vector<int> indices = ...; // lookup_tableを参照としてキャプチャし、 // lookup_tableの要素に関連づけられたindicesの値をソートしています。 std::sort(indices.begin(), indices.end(), [&](int a, int b) { return lookup_table[a] < lookup_table[b]; });
A variable capture can also have an explicit initializer, which can be used for capturing move-only variables by value, or for other situations not handled by ordinary reference or value captures: 変数のキャプチャには、明示的な初期化子を与えることもできます。 これによって、ムーブのみ可能な型の変数を値キャプチャしたいときや、通常の参照キャプチャや値キャプチャでは対処できないその他の状況にも対応できます。
std::unique_ptr<Foo> foo = ...; [foo = std::move(foo)] () { ... }
Such captures (often called "init captures" or "generalized lambda captures") need not actually "capture" anything from the enclosing scope, or even have a name from the enclosing scope; this syntax is a fully general way to define members of a lambda object: このようなキャプチャ (「初期化キャプチャ」または「一般化されたラムダキャプチャ」と呼ばれる) は、実際には、それを囲むスコープから何かをキャプチャする必要はなく、囲んでいるスコープ内に名前がある必要もありません。つまり、この構文は、ラムダオブジェクトのメンバ変数を定義するための一般的な手段として用いることができます。
[foo = std::vector<int>({1, 2, 3})] () { ... }
The type of a capture with an initializer is deduced using the same rules
as auto
.
初期化子を使用したキャプチャの型は、auto
と同じ規則によって推論されます。
- Lambdas are much more concise than other ways of defining function objects to be passed to STL algorithms, which can be a readability improvement. ラムダ式は、STLアルゴリズムに渡す関数オブジェクトを定義する手段として非常に簡潔であり、可読性も向上させます。
- Appropriate use of default captures can remove redundancy and highlight important exceptions from the default. 適切にデフォルトキャプチャを用いることで、冗長性を減らし、また、デフォルトとは異なる重要な部分を目立たせることができます。
-
Lambdas,
std::function
, andstd::bind
can be used in combination as a general purpose callback mechanism; they make it easy to write functions that take bound functions as arguments. ラムダやstd::function
、std::bind
は、一般的なコールバックの仕組みとして組み合わせて使用することもでき、これによって、引数に関数をとる関数が書きやすくなります。
- Variable capture in lambdas can be a source of dangling-pointer bugs, particularly if a lambda escapes the current scope. ラムダの変数のキャプチャは、ダングリングポインタバグの元になる危険性があります。特にラムダがスコープ外に出ていくときは注意が必要です。
-
Default captures by value can be misleading because they do not prevent
dangling-pointer bugs.
誤解されやすいですが、値のデフォルトキャプチャが、ダングリングポインタのバグを防ぐことはありません。
Capturing a pointer by value doesn't cause a deep
copy, so it often has the same lifetime issues as capture by reference.
ポインタを値キャプチャしても、ポインタが差す先のオブジェクトがコピーされるわけではないため、変数を参照キャプチャした場合と同様に、オブジェクトの寿命に関して気を配らなければなりません。
This is especially confusing when capturing 'this' by value, since the use
of 'this' is often implicit.
特に、
this
ポインタは暗黙的に使用されることも多く、これを暗黙的に値キャプチャしてしまうと混乱の元になりがちです。 -
Captures actually declare new variables (whether or not the captures have
initializers), but they look nothing like any other variable declaration
syntax in C++.
実際のところ、キャプチャとは、その初期化子の有無に関係なく、新しい変数を宣言することなのですが、それらの宣言は、C++の他の変数宣言構文とまったく異なる見た目をしています。
In particular, there's no place for the variable's type,
or even an
auto
placeholder (although init captures can indicate it indirectly, e.g., with a cast). 特に、キャプチャの変数宣言には型名がなく、auto
キーワードすら表れません (ただし、初期化キャプチャは、キャストなどを使用して間接的に型を示すことができます)。 This can make it difficult to even recognize them as declarations. このことは、それらが変数宣言であることを見分けるのを困難にするかもしれません。 -
Init captures inherently rely on type
deduction, and suffer from many of the same drawbacks as
auto
, with the additional problem that the syntax doesn't even cue the reader that deduction is taking place. 初期化子つきのキャプチャは本質的に型推論に依存しており、auto
の持つ欠点の多くを同様に抱えています。 また、その構文において、そこで型推論が行われていること自体をコード読者に伝えることもできないという点も問題です。 - It's possible for use of lambdas to get out of hand; very long nested anonymous functions can make code harder to understand. ラムダは、手に余るような扱い方もできてしまいます。 もし、ネストされた非常に長い匿名の関数があったら、それらはコードを理解する際の妨げとなるでしょう。
- Use lambda expressions where appropriate, with formatting as described below. ラムダ式は適切に使用してください。 ラムダ式を使用する場合は、ラムダ式の書式に従ってください。
-
Prefer explicit captures if the lambda may escape the current scope.
ラムダを現在のスコープの外に出す場合は、変数のキャプチャを明示的に行いましょう。
For example, instead of:
以下に例を示します。悪い例:
{ Foo foo; ... executor->Schedule([&] { Frobnicate(foo); }) ... } // BAD! The fact that the lambda makes use of a reference to `foo` and // possibly `this` (if `Frobnicate` is a member function) may not be // apparent on a cursory inspection. If the lambda is invoked after // the function returns, that would be bad, because both `foo` // and the enclosing object could have been destroyed.
{ Foo foo; ... executor->Schedule([&] { Frobnicate(foo); }) ... } // これはダメ! // `foo`と`this`(`Frobnicate`がメンバ関数である場合)の参照が作られますが、 // 一見しただけでは、そのことに気づかないかもしれません。 // もし、Schedule関数から処理が戻った後で、このラムダが呼び出されると、 // よくないことが起こるでしょう。ラムダが呼び出されるそのときには、`foo`も、 // このラムダを取り囲んでいたオブジェクトも、破壊済みかもしれません。
{ Foo foo; ... executor->Schedule([&foo] { Frobnicate(foo); }) ... } // BETTER - The compile will fail if `Frobnicate` is a member // function, and it's clearer that `foo` is dangerously captured by // reference.
{ Foo foo; ... executor->Schedule([&foo] { Frobnicate(foo); }) ... } // こちらの方が良いです。 // `Frobnicate`がメンバ関数である場合、コンパイルに失敗します。 // `foo`についても、危険な参照キャプチャをされていることがコードから明確です。
-
Use default capture by reference ([&]) only when the
lifetime of the lambda is obviously shorter than any potential
captures.
参照のデフォルトキャプチャ(
[&]
)は、ラムダの寿命が、すべての潜在的なキャプチャよりも明らかに短いときにのみ使用してください。 -
Use default capture by value ([=]) only as a means of binding a
few variables for a short lambda, where the set of captured
variables is obvious at a glance, and which does not result in
capturing
this
implicitly. 値のデフォルトキャプチャ([=]
)は、短いラムダにおいて1~2個の変数がキャプチャされる場合で、一目見ればどの変数がキャプチャされているかわかり、かつ、暗黙的なthis
のキャプチャが行われない場合にのみ使用してください。 (That means that a lambda that appears in a non-static class member function and refers to non-static class members in its body must capturethis
explicitly or via[&]
.) (つまり、ラムダが、クラスのメンバ関数内にあって、かつ、何らかの非静的メンバを参照する場合は、必ずthis
を明示的にキャプチャするか、[&]
を使用しなければならないということです。) Prefer not to write long or complex lambdas with default capture by value. 長いラムダや複雑なラムダにおいては、値のデフォルトキャプチャを行うのは望ましくありません。 - Use captures only to actually capture variables from the enclosing scope. キャプチャは、自身を囲むスコープから実際に変数をキャプチャするためだけに使用してください。 Do not use captures with initializers to introduce new names, or to substantially change the meaning of an existing name. 新しい名前を導入する目的や、あるいは、既存の名前の意味を変える目的で、初期化子つきキャプチャを使ってはいけません。 Instead, declare a new variable in the conventional way and then capture it, or avoid the lambda shorthand and define a function object explicitly. そのかわりに、従来ながらの方法で新しいローカル変数を宣言してそれをキャプチャするか、ラムダによる短縮表現を使うのをやめて明示的に通常の関数オブジェクトを定義してください。
- See the section on type deduction for guidance on specifying the parameter and return types. 引数や戻り値の型の指定方法に関するガイドとして型推論のセクションも参照してください。
テンプレートメタプログラミング
Avoid complicated template programming. 複雑なテンプレートプログラミングは避けましょう。
Template metaprogramming refers to a family of techniques that exploit the fact that the C++ template instantiation mechanism is Turing complete and can be used to perform arbitrary compile-time computation in the type domain. テンプレートメタプログラミングとは、C++のテンプレートのインスタンス化の仕組みがチューリング完全であり、コンパイル時に型の世界で任意の計算を行うことができるという事実を利用した一連のテクニックのことを指します。
Template metaprogramming allows extremely flexible interfaces that
are type safe and high performance.
テンプレートメタプログラミングは、型安全かつ高性能で非常に柔軟なインターフェースを提供します。
Facilities like
GoogleTest,
std::tuple
, std::function
, and
Boost.Spirit would be impossible without it.
GoogleTest
やstd::tuple
、std::function
、Boost.Spiritのような機能は、テンプレートメタプログラミングなしには実現できません。
The techniques used in template metaprogramming are often obscure to anyone but language experts. テンプレートメタプログラミングにおいて使われるテクニックは、言語の専門家以外にはわかりにくいものになりがちです。 Code that uses templates in complicated ways is often unreadable, and is hard to debug or maintain. 複雑な手法のテンプレートを含むコードは、可読性が低く、デバッグやメンテナンスも難しくなります。
Template metaprogramming often leads to extremely poor compile time error messages: even if an interface is simple, the complicated implementation details become visible when the user does something wrong. テンプレートメタプログラミングは、コンパイル時のエラーメッセージを非常に貧弱なものにしがちです。たとえ、インターフェースがシンプルに保たれていたとしても、ひとたびユーザーが何かを間違えた途端に、複雑な内部実装が大量のエラーメッセージとともに現れてしまうことがあります。
Template metaprogramming interferes with large scale refactoring by making the job of refactoring tools harder. テンプレートメタプログラミングは、リファクタリングツールの動作を難しくするため、大規模なリファクタリングの妨げとなることがあります。 First, the template code is expanded in multiple contexts, and it's hard to verify that the transformation makes sense in all of them. 第一に、テンプレートコードは様々な文脈で展開されますが、展開後のすべての箇所において、それらが正しい意味を持つか検証することが難しいです。 Second, some refactoring tools work with an AST that only represents the structure of the code after template expansion. 第二に、リファクタリングツールの中には、テンプレート展開後のコード構造を表すASTしか扱えないものが存在します。 It can be difficult to automatically work back to the original source construct that needs to be rewritten. このようなツールでは、本来のリファクタリング対象である展開前のソースコードを自動的に書き換えることはできないかもしれません。
Template metaprogramming sometimes allows cleaner and easier-to-use interfaces than would be possible without it, but it's also often a temptation to be overly clever. テンプレートメタプログラミングは、使い方次第では、インターフェースをきれいで使いやすくするために役立ちます。しかし、それと同時に、必要以上に巧妙な実装をさせようとする挑戦への誘惑のかたまりでもあります。 It's best used in a small number of low level components where the extra maintenance burden is spread out over a large number of uses. テンプレートメタプログラミングは、そのコードの高いメンテナンスコストをたくさんの使用箇所で分散できるような少数の低レベルコンポーネントで用いるのが最適でしょう。
Think twice before using template metaprogramming or other
complicated template techniques; think about whether the average
member of your team will be able to understand your code well enough
to maintain it after you switch to another project, or whether a
non-C++ programmer or someone casually browsing the code base will be
able to understand the error messages or trace the flow of a function
they want to call.
テンプレートメタプログラミングや、それに類する複雑なテンプレートテクニックを導入しようとする前に、一度立ち止まって次のことを考えてみてください。あなたのプロジェクトチームの平均的なメンバーは、あなたがチームを抜けた後でも、そのコードを十分に理解してメンテナンスできそうでしょうか? あるいは、C++を専門としないプログラマーや他の誰かが、カジュアルにそのコードベースを眺めたときに、エラーメッセージを理解したり、呼び出したい関数のプログラムの流れを追うことができそうでしょうか?
If you're using recursive template instantiations
or type lists or metafunctions or expression templates, or relying on
SFINAE or on the sizeof
trick for detecting function
overload resolution, then there's a good chance you've gone too
far.
もしあなたが、再帰的に、テンプレートのインスタンス化、型リスト、メタ関数、式テンプレートなどを使おうとしていたり、あるいは、SFINAEや、関数オーバーロード解決のためのsizeof
トリックを使おうとしているのならば、それらは、おそらくやり過ぎであることを示しているでしょう。
If you use template metaprogramming, you should expect to put considerable effort into minimizing and isolating the complexity. テンプレートメタプログラミングを使うのであれば、その複雑さを最小化して分離することに対して、かなりの労力を費やすことになるでしょう。 You should hide metaprogramming as an implementation detail whenever possible, so that user-facing headers are readable, and you should make sure that tricky code is especially well commented. メタプログラミングのコードは、実装の詳細として可能な限り隠し、ユーザーが目にするヘッダーファイルを可能な限り読みやすく保ってください。 また、トリッキーなコードについては、特にしっかりとコメントを残すようにしてください。 You should carefully document how the code is used, and you should say something about what the "generated" code looks like. それらのコードがどのように使われるのかに注意してドキュメント化し、展開後のコードがどのような外観を持つのかについても言及してください。 Pay extra attention to the error messages that the compiler emits when users make mistakes. コードのユーザーが何かを間違えたときにコンパイラが生成するであろうエラーメッセージに、特別の気を配ってください。 The error messages are part of your user interface, and your code should be tweaked as necessary so that the error messages are understandable and actionable from a user point of view. これらのエラーメッセージはAPIにおけるユーザーインターフェースの一部なのです。 コードを使用するユーザーが何かを間違えてしまったとき、何が間違っていて何をすればいいのかわかるようなエラーメッセージが生成されるようにコードを調整してください。
コンセプトと制約
Use concepts sparingly.
コンセプトは控えめに。
In general, concepts and constraints should only be used in cases
where templates would have been used prior to C++20.
一般に、コンセプトと制約は、以前のC++であればテンプレートを使っていた場面に限って使用します。
Avoid introducing new concepts in headers,
unless the headers are marked as internal to the library.
ライブラリ内部でのみ使用するとマークしている場合を除いて、公開ヘッダーファイル内で新しいコンセプトを導入するのは避けてください。
Do not define concepts that are not enforced by the compiler.
また、コンパイラによる強制が働かないコンセプトも定義してはいけません。
Prefer constraints over template metaprogramming, and
avoid the template<Concept T>
syntax;
instead, use the requires(Concept<T>)
syntax.
型に制約を課すときは、従来形式のテンプレートメタプログラミングよりも、新しい制約の機能を優先的に使用してください。
また、その際には、template<Concept T>
の形の構文は避け、かわりにrequires(Concept<T>)
の形を使ってください。
The concept
keyword is a new mechanism for defining
requirements (such as type traits or interface specifications)
for a template parameter.
concept
キーワードは、テンプレート引数に対して、型トレイトやインターフェース仕様等の要件を定義する新たな仕組みです。
The requires
keyword provides mechanisms for placing
anonymous constraints on templates and verifying that constraints
are satisfied at compile time.
requires
キーワードは、テンプレートにおいて無名の制約を課すための新たな仕組みであり、また、その制約が満たされていかどうかコンパイル時の検証を行わせるためにも使われます。
Concepts and constraints are often used together, but can be
also used independently.
コンセプトと制約は多くの場合に同時に用いられますが、それぞれを独立して用いることもできます。
- Concepts allow the compiler to generate much better error messages when templates are involved, which can reduce confusion and significantly improve the development experience. コンセプトによって、テンプレートを使ったコードにおけるコンパイル時エラーメッセージを大きく改善することができます。これによって、混乱が軽減され、開発時のエクスペリエンスが大幅に向上します。
- Concepts can reduce the boilerplate necessary for defining and using compile-time constraints, often increasing the clarity of the resulting code. コンセプトによって、コンパイル時に制約を課すために必要となる定型文コードを減らすことができます。これによって、コードの意図がより明確になります。
- Constraints provide some capabilities that are difficult to achieve with templates and SFINAE techniques. 制約によって、従来のテンプレートやSFINAEテクニックでは実現が難しかった条件を表現できるようになります。
- As with templates, concepts can make code significantly more complex and difficult to understand. テンプレートと同様ですが、コンセプトを導入することによって、コードが著しく複雑化し、理解しにくいものになってしまうかもしれません。
- Concept syntax can be confusing to readers, as concepts appear similar to class types at their usage sites. コンセプトの文法は、それを使う側のコードにおいてクラス型のような見た目をしているため、コードの読者を混乱させるかもしれません。
- Concepts, especially at API boundaries, increase code coupling, rigidity, and ossification. コンセプトは、特にAPI境界において用いた場合に、コードの結合性や硬化性を高めてしまいます。
- Concepts and constraints can replicate logic from a function body, resulting in code duplication and increased maintenance costs. コンセプトと制約は、関数本文のロジックの複製となることがあります。このことは、コードの重複を増やし、メンテナンスコストの増大にも繋がります。
- Concepts muddy the source of truth for their underlying contracts, as they are standalone named entities that can be utilized in multiple locations, all of which evolve separately from each other. コンセプトは、それぞれが複数の場所で使用できる独立したエンティティであるため、それぞれの場所でばらばらに成長してしまうことによって、それらの根底にある信頼できる契約の情報源を濁してしまうことがあります。 This can cause the stated and implied requirements to diverge over time. これによって、記述された要件と本来の暗黙的要件とを、時とともに乖離させていってしまうかもしれません。
- Concepts and constraints affect overload resolution in novel and non-obvious ways. コンセプトと制約は、オーバーロード解決においても、奇抜で、わかりにくい影響を与えます。
- As with SFINAE, constraints make it harder to refactor code at scale. SFINAEと同様に、制約はコードの大規模なリファクタリングをより困難にします。
Predefined concepts in the standard library should be
preferred to type traits, when equivalent ones exist.
標準ライブラリにおいて、目的の型トレイトと同様のコンセプトとの両方が定義されているときは、コンセプトの方を優先的に用いましょう。
(e.g., if std::is_integral_v
would have been used
before C++20, then std::integral
should be used in
C++20 code.)
(たとえば、今までstd::is_integral_v
を使っていた箇所では、C++20ではstd::integral
を使います。)
Similarly, prefer modern constraint syntax
(via requires(Condition)
).
同様に、新しい制約の構文を、(requires(Condition)
の形式で)優先的に用いましょう。
Avoid legacy template metaprogramming constructs
(such as std::enable_if<Condition>
)
as well as the template<Concept T>
syntax.
template<Concept T>
の構文や、
std::enable_if<Condition>
のような従来形式のテンプレートメタプログラミングは避けましょう。
Do not manually re-implement any existing concepts or traits.
既存のコンセプトやトレイトを再実装してはいけません。
For example, use
requires(std::default_initializable<T>)
instead of
requires(requires { T v; })
or the like.
たとえば、requires(requires { T v; })
のようなコードを書くのではなく、既存のrequires(std::default_initializable<T>)
を使うようにしてください。
New concept
declarations should be rare, and only
defined internally within a library, such that they are not
exposed at API boundaries.
新しいconcept
を宣言することは控えましょう。新たにコンセプトを定義する場合であっても、ライブラリ内での内部利用にとどめ、API境界を越えてそれらが公開されることがないようにしてください。
More generally, do not use concepts or constraints in cases where
you wouldn't use their legacy template equivalents in C++17.
より一般には、今までのC++において同様のことをするテンプレートを使うべきでなかった箇所においては、同じくコンセプトや制約を使うべきではないということです。
Do not define concepts that duplicate the function body, or impose requirements that would be insignificant or obvious from reading the body of the code or the resulting error messages. 関数本体の複製となるようなコンセプトを定義してはいけません。 また、コード本体やエラーメッセージを読めば自明にわかるような、取るに足らない要件を課してもいけません。 For example, avoid the following:たとえば、次のようなコードは避けてください。
template <typename T> // Bad - redundant with negligible benefit concept Addable = std::copyable<T> && requires(T a, T b) { a + b; }; template <Addable T> T Add(T x, T y, T z) { return x + y + z; }
template <typename T> // 悪い例。冗長だし、メリットもない concept Addable = std::copyable<T> && requires(T a, T b) { a + b; }; template <Addable T> T Add(T x, T y, T z) { return x + y + z; }
Instead, prefer to leave code as an ordinary template unless you can demonstrate that concepts result in significant improvement for that particular case, such as in the resulting error messages for a deeply nested or non-obvious requirement. このようなケースでは、コンセプトは使わず、従来なからのただのテンプレート関数のままにしておく方が望ましいです。 コンセプトは、コンセプトを導入することによって、状況を大きく改善できる場合に限って用いるようにしてください。 たとえば、深くネストされた要件や、一目ではわかりにくいような要件などに対して、そのエラーメッセージを改善する目的などには、コンセプトは適役でしょう。
Concepts should be statically verifiable by the compiler. すべてのコンセプトは、コンパイラによって静的に検証可能なものでなければなりません。 Do not use any concept whose primary benefits would come from a semantic (or otherwise unenforced) constraint. 意味的な(あるいは、コンパイル時に強制されないような)制約条件によって主な利益がもたらされるコンセプトは使用してはいけません。 Requirements that are unenforced at compile time should instead be imposed via other mechanisms such as comments, assertions, or tests. コンパイル時のチェックが働かない(あるいは、できない)要件については、コンセプトや制約ではなく、コードコメントやアサーション、テストなどでそれらをカバーするようにしてください。
C++20 モジュール
Do not use C++20 Modules. C++20のモジュールは使わないでください。
C++20 introduces "modules", a new language feature designed as an alternative to textual inclusion of header files.
C++20では「モジュール」という新しい言語機能が導入されました。これは、従来のヘッダファイルの物理的なインクルードを置き換えるように設計されています。
It introduces three
new keywords to support
this: module
, export,
and import
.
これによって新しい3つのキーワードmodule
, export
, import
が導入されました。
Modules are a big shift in how C++ is written and compiled, and we are still assessing how they may fit into Google's C++ ecosystem in the future. 「モジュール」はC++がどのように書かれてどのようにコンパイルされるかという側面における大きな変革であり、現時点では、私たちはこの機能が将来のGoogleのC++エコシステムにおいてどのように適合するかを評価している段階です。 Furthermore, they are not currently well-supported by our build-systems, compilers, and other tooling, and need further exploration as to the best-practices when writing and using them. また、現時点では、ビルドシステムやコンパイラ、その他のツールにおいても十分なサポートがなく、それらを書いたり使ったりする際のベストプラクティスについて、さらなる調査を必要としています。
コルーチン
Do not use coroutines (yet). コルーチンは(まだ)使わないでください。
Do not include the <coroutine>
header,
or use the co_await
, co_yield
,
or co_return
keywords.
<coroutine>
ヘッダーをインクルードしないでください。また、co_await
やco_yield
、co_return
といったキーワードを使用しないでください。
NOTE: this ban is expected to be temporary, while further guidance is being developed. 注: この制限事項はさらなるガイダンスか策定されるまでの間の一時的なものです。
Boost
Use only approved libraries from the Boost library collection. Boostライブラリからは、そのライブラリコレクションのうち、あらかじめ認められたもののみを使うことができます。
The Boost library collection is a popular collection of peer-reviewed, free, open-source C++ libraries. Boostライブラリコレクションは、ピアレビュー済み、フリー、オープンソースの、有名なC++ライブラリのコレクションです。
Boost code is generally very high-quality, is widely portable, and fills many important gaps in the C++ standard library, such as type traits and better binders. Boostのコードは、全般にとても品質が良く、広い移植性があり、型トレイトや優れたバインダなどによって、C++標準ライブラリ内に散在するたくさんの隙間を埋めてくれます。
Some Boost libraries encourage coding practices which can hamper readability, such as metaprogramming and other advanced template techniques, and an excessively "functional" style of programming. いくつかのBoostライブラリでは、メタプログラミングやその他の先進的なテンプレートテクニックを積極的に取り入れていたり、関数型プログラミング言語のスタイルを採用していたりします。これらの手法はコードの可読性を妨げます。
In order to maintain a high level of readability for all contributors who might read and maintain code, we only allow an approved subset of Boost features. コードを読み、メンテナンスするすべてのコントリビュータのために、コードは高い可読性が保たれていなければなりません。この可読性を維持するために、Boostからは一部のサブセットのみについて、使用することを認めます。 Currently, the following libraries are permitted: 現時点では、以下のライブラリが使用を認められています。
-
Call Traits
from
boost/call_traits.hpp
Call Traits (boost/call_traits.hpp
) -
Compressed Pair
from
boost/compressed_pair.hpp
Compressed Pair (boost/compressed_pair.hpp
) -
The Boost Graph Library (BGL)
from
boost/graph
, except serialization (adj_list_serialize.hpp
) and parallel/distributed algorithms and data structures (boost/graph/parallel/*
andboost/graph/distributed/*
). The Boost Graph Library (BGL) (boost/graph
) ただし、serialization (adj_list_serialize.hpp
)と、 parallel/distributed algorithms and data structures (boost/graph/parallel/*
,boost/graph/distributed/*
)を除く -
Property Map
from
boost/property_map
, except parallel/distributed property maps (boost/property_map/parallel/*
). Property Map(boost/property_map
) ただし、parallel/distributed property maps (boost/property_map/parallel/*
) を除く -
Iterator
from
boost/iterator
Iterator (boost/iterator
) -
The part of
Polygon
that deals with Voronoi diagram
construction and doesn't depend on the rest of
Polygon:
boost/polygon/voronoi_builder.hpp
,boost/polygon/voronoi_diagram.hpp
, andboost/polygon/voronoi_geometry_type.hpp
Polygonのうち、Voronoi diagram constructionを扱い、かつ残りのPolygonに依存していない部分 (boost/polygon/voronoi_builder.hpp
,boost/polygon/voronoi_diagram.hpp
,boost/polygon/voronoi_geometry_type.hpp
) -
Bimap
from
boost/bimap
Bimap (boost/bimap
) -
Statistical Distributions and Functions
from
boost/math/distributions
Statistical Distributions and Functions (boost/math/distributions
) -
Special Functions
from
boost/math/special_functions
Special Functions (boost/math/special_functions
) -
Root Finding & Minimization Functions
from
boost/math/tools
Root Finding & Minimization Functions (boost/math/tools
) -
Multi-index
from
boost/multi_index
Multi-index (boost/multi_index
) -
Heap
from
boost/heap
Heap (boost/heap
) -
The flat containers from
Container:
boost/container/flat_map
, andboost/container/flat_set
ContainerのThe flat containers (boost/container/flat_map
,boost/container/flat_set
) -
Intrusive
from
boost/intrusive
. Intrusive (boost/intrusive
) -
The
boost/sort
library . Theboost/sort
library -
Preprocessor
from
boost/preprocessor
. Preprocessor (boost/preprocessor
)
We are actively considering adding other Boost features to the list, so this list may be expanded in the future. なお、他のBoostの機能についても、随時リストへの追加を検討していますので、将来的にこのリストは拡張されることがあります。
使用禁止とする標準ライブラリの機能
As with Boost, some modern C++ library functionality encourage coding practices that hamper readability—for example by removing checked redundancy (such as type names) that may be helpful to readers, or by encouraging template metaprogramming. 前述のBoostも含め、現代的なC++ライブラリの中には、可読性の妨げとなるようなコーディング手法を推奨しているものもあります。 たとえば、型名などの読者の助けになるはずの情報を冗長であるとして省いてしまっていたり、テンプレートメタプログラミングを積極的に採用していたりします。 Other extensions duplicate functionality available through existing mechanisms, which may lead to confusion and conversion costs. その他の様々な拡張は、既存の仕組みでも実現可能なことの繰り返しであり、これらは困惑の元になったり議論のコストを招いたりします。
The following C++ standard library features may not be used: 以下に挙げるC++標準ライブラリの機能は使ってはいけません。
-
Compile-time rational numbers
(
<ratio>
), because of concerns that it's tied to a more template-heavy interface style. コンパイル時有理数(<ratio>
ヘッダー)。 これらの機能を使うと、インターフェースが、よりテンプレートを多用するスタイルに繋がる懸念があります。 -
The
<cfenv>
and<fenv.h>
headers, because many compilers do not support those features reliably.<cfenv>
と<fenv.h>
ヘッダー。 これらの機能は、多くのコンパイラにおいて信頼性がありません。 -
The
<filesystem>
header, which does not have sufficient support for testing, and suffers from inherent security vulnerabilities.<filesystem>
ヘッダー。 これらの機能は、テストに対するサポートが不十分であり、また、その性質上セキュリティの脆弱性につながりやすくもあります。
非標準の拡張
Nonstandard extensions to C++ may not be used unless otherwise specified. C++非標準の拡張は、特別に認められたものを除いて使ってはいけません。
Compilers support various extensions that are not part of standard C++.
コンパイラは、様々なC++非標準の拡張を提供しています。
Such
extensions include GCC's __attribute__
, intrinsic functions such
as __builtin_prefetch
or SIMD, #pragma
, inline
assembly, __COUNTER__
, __PRETTY_FUNCTION__
,
compound statement expressions (e.g., foo = ({ int x; Bar(&x); x
})
, variable-length arrays and alloca()
, and the
"Elvis
Operator" a?:b
..
このような非標準の拡張には、たとえばGCCがサポートしている、__attribute__
、__builtin_prefetch
やSIMDのような組み込み関数、#pragma
、インラインアセンブリ、__COUNTER__
、__PRETTY_FUNCTION__
、foo = ({ int x; Bar(&x); x })
のような複合文、可変長配列とalloca()
、エルビス演算子(a?:b
)などが含まれます。
- Nonstandard extensions may provide useful features that do not exist in standard C++. 非標準の拡張によって、標準のC++の範囲では実現できない便利な機能が提供されます。
- Important performance guidance to the compiler can only be specified using extensions. 重要なパフォーマンス上のガイドをコンパイラに伝える方法は、拡張機能を使う以外にありません。
- Nonstandard extensions do not work in all compilers. 非標準の拡張は、すべてのコンパイラで機能するわけではありません。 Use of nonstandard extensions reduces portability of code. このような拡張機能を使用すると、コードの移植性が低下します。
- Even if they are supported in all targeted compilers, the extensions are often not well-specified, and there may be subtle behavior differences between compilers. ターゲットとするすべてのコンパイラにおいて目的の拡張機能がサポートされている場合でも、それらの仕様が十分に定められていなかったり、コンパイラ間で微妙に挙動が異なったりすることがあります。
- Nonstandard extensions add to the language features that a reader must know to understand the code. 非標準の拡張の導入によって、コードで使用される言語機能が増えると、コードの読者が学ばなければいけないことも増えます。
- Nonstandard extensions require additional work to port across architectures. 非標準の拡張が使用されていると、アーキテクチャ間をまたいだ移植の際に、追加の作業を必要とします。
Do not use nonstandard extensions. 非標準の拡張を使ってはいけません。 You may use portability wrappers that are implemented using nonstandard extensions, so long as those wrappers are provided by a designated project-wide portability header. なお、プロジェクトにおいて、非標準拡張機能を含む移植性の問題を解決することを目的とした、プロジェクト全体で使用するラッパーライブラリがある場合は、それらを通してこれらの機能を使用する分にはかまいません。
エイリアス
Public aliases are for the benefit of an API's user, and should be clearly documented. APIのユーザーのためのエイリアスのみを公開します。 また、公開するエイリアスについては明確なドキュメントを用意します。
There are several ways to create names that are aliases of other entities: C++において、他のエンティティへのエイリアスとして新しい名前を付けるには、いくつか方法があります。
typedef Foo Bar; // But prefer `using` in C++ code. using ::other_namespace::Foo; using enum MyEnumType; // Creates aliases for all enumerators in MyEnumType.
typedef Foo Bar; // C++では `using` を使う方が好ましい using ::other_namespace::Foo; using enum MyEnumType; // MyEnumTypeの全列挙子のエイリアスを作る
In new code, using
is preferable to typedef
,
because it provides a more consistent syntax with the rest of C++ and works
with templates.
新しく書くコードにおいては、typedef
よりもusing
を使う方が望ましいです。
using
の方が、よりC++の他の文法との一貫性があり、また、テンプレートとの相性も良いためです。
Like other declarations, aliases declared in a header file are part of that
header's public API unless they're in a function definition, in the private portion of a class,
or in an explicitly-marked internal namespace. Aliases in such areas or in .cc
files
are implementation details (because client code can't refer to them), and are not restricted by
this rule.
他の宣言と同様に、ヘッダーファイルにおけるエイリアスの宣言は、その宣言が公開部分にある場合、同ヘッダーが提供するAPIの一部と見なされます。
一方で、エイリアスの宣言がクライアントコードから参照できない場所にある場合、たとえば、.cc
ファイル内にある場合や、ヘッダーファイル内であっても、関数内部や、クラスのprivate
セクション、明示的に内部用と宣言された名前空間内に存在する場合は、それらの宣言は実装の詳細と見なされ、このルールで課す制限の対象外となります。
- Aliases can improve readability by simplifying a long or complicated name. エイリアスを用いると、長く複雑な名前を単純化できるため、コードの可読性を向上させることができます。
- Aliases can reduce duplication by naming in one place a type used repeatedly in an API, which might make it easier to change the type later. エイリアスを用いると、APIによって何度も繰り返し使われる型の名前を一カ所で命名できるため、コードの重複を減らすことができます。 また、これによって、将来、その型を変更する機会が生じたときに、その作業を進めやすくなるかもしれません。
- When placed in a header where client code can refer to them, aliases increase the number of entities in that header's API, increasing its complexity. クライアントコードから参照可能なヘッダーファイル内でのエイリアス宣言は、そのヘッダーが提供するAPIのエンティティの数を増やすため、APIを複雑化させる要因になります。
- Clients can easily rely on unintended details of public aliases, making changes difficult. クライアントコードは、公開エイリアスの意図しない詳細に容易に依存してしまえるため、将来の変更を難しくする要因になります。
- It can be tempting to create a public alias that is only intended for use in the implementation, without considering its impact on the API, or on maintainability. エイリアスを使うことを認めると、APIやそのメンテナンス性に及ぼす影響をよく考えないまま、実装のためだけに使う余分な公開エイリアスを作ってしまうことを誘引する要因になります。
- Aliases can create risk of name collisions エイリアスは名前衝突のリスクの要因になります。
- Aliases can reduce readability by giving a familiar construct an unfamiliar name 馴染みのある構造に対して、馴染みのない名前をつけてしまうと、コードの可読性を低下させる要因になります。
- Type aliases can create an unclear API contract: it is unclear whether the alias is guaranteed to be identical to the type it aliases, to have the same API, or only to be usable in specified narrow ways 型のエイリアスは、APIの規約を不明確にする要因になります。 ある型エイリアスが、そのエイリアス先の型と常に同等で同じAPIを持つことが保証されているのか、あるいは、何らか所定の狭い方法でのみ使うことを目的としているのかが、明確でなくなってしまうことがあります。
Don't put an alias in your public API just to save typing in the implementation; do so only if you intend it to be used by your clients. APIの公開部分には、クライアントコードによって使用されることを意図したエイリアスのみを宣言してください。それ以外の、たとえば実装時のタイピング数を減らす目的では、公開APIにエイリアスを宣言してはいけません。
When defining a public alias, document the intent of the new name, including whether it is guaranteed to always be the same as the type it's currently aliased to, or whether a more limited compatibility is intended. 公開エイリアスを定義するときは、そこで新しい名前を導入する目的をドキュメントに記述してください。 その際、エイリアスが、エイリアス先の型と常に同等であることが保証されているのか、あるいは、それよりももっと限定された互換性の範囲内でのみ使われることを想定して定義されているのかについての説明を含めてください。 This lets the user know whether they can treat the types as substitutable or whether more specific rules must be followed, and can help the implementation retain some degree of freedom to change the alias. これによって、APIのユーザーは、それらのエイリアスを型の単純な置き換えとして扱ってよいのか、あるいは、何らかのルールに従う必要があるのか判断することができるようになります。 同時に、その実装において、エイリアスの変更に対する一定の自由度を確保することもできるようになるでしょう。
Don't put namespace aliases in your public API. (See also Namespaces). 名前空間へのエイリアスを公開APIに含めてはいけません(名前空間を参照)。
For example, these aliases document how they are intended to be used in client code: 以下のコード例では、それぞれのエイリアスがクライアントコードからどのように扱われることを意図しているかについてドキュメント化しています。
namespace mynamespace { // Used to store field measurements. DataPoint may change from Bar* to some internal type. // Client code should treat it as an opaque pointer. using DataPoint = ::foo::Bar*; // A set of measurements. Just an alias for user convenience. using TimeSeries = std::unordered_set<DataPoint, std::hash<DataPoint>, DataPointComparator>; } // namespace mynamespace
namespace mynamespace { // フィールドの測定値を保存するために使います。 // DataPoint は Bar* から他の内部表現用の型に変更されるかもしれません。 // クライアントコードにおいては、これを透過的なポインタとして扱ってください。 using DataPoint = ::foo::Bar*; // 測定値のセットを表します。 // このエイリアスは、単に利便性のために定義されています。 using TimeSeries = std::unordered_set<DataPoint, std::hash<DataPoint>, DataPointComparator>; } // namespace mynamespace
These aliases don't document intended use, and half of them aren't meant for client use: 一方で、次の例では、エイリアスの目的に関する記述がなく、また定義の半分についてはクライアントコードから使われることを想定したものですらありません。
namespace mynamespace { // Bad: none of these say how they should be used. using DataPoint = ::foo::Bar*; using ::std::unordered_set; // Bad: just for local convenience using ::std::hash; // Bad: just for local convenience typedef unordered_set<DataPoint, hash<DataPoint>, DataPointComparator> TimeSeries; } // namespace mynamespace
namespace mynamespace { // 悪い例: どのように扱われるべきか書かれていない。 using DataPoint = ::foo::Bar*; using ::std::unordered_set; // ダメ: 実装の利便性のためだけに定義されている using ::std::hash; // ダメ: 実装の利便性のためだけに定義されている typedef unordered_set<DataPoint, hash<DataPoint>, DataPointComparator> TimeSeries; } // namespace mynamespace
However, local convenience aliases are fine in function definitions, private
sections of
classes, explicitly marked internal namespaces, and in .cc
files:
ただし、関数定義の中など、クライアントコードからアクセスできない場所であれば、このような実装の利便性のためのエイリアスを定義してもかまいません。同様に、クラスのprivate
セクション、明示的に内部利用とマークされた名前空間の中、.cc
ファイル内などにおける定義も同様に問題ありません。
// In a .cc file using ::foo::Bar;
// .ccファイル内 using ::foo::Bar;
switch文
If not conditional on an enumerated value, switch statements should always
have a default
case (in the case of an enumerated value, the
compiler will warn you if any values are not handled).
switch
文の条件式が列挙型でない場合は、必ずdefault
ブロックが必要です。
(列挙型の値によるswitch
文については、対応漏れがある場合にコンパイラが警告してくれるでしょう。)
If the default case
should never execute, treat this as an error. For example:
プログラムの構造上、default
ブロックが実行される機会は絶対にないという場合には、次の例のようにエラーとして扱うようにしてください。
switch (var) { case 0: { ... break; } case 1: { ... break; } default: { LOG(FATAL) << "Invalid value in switch statement: " << var; } }
Fall-through from one case label to another must be annotated using the
[[fallthrough]];
attribute.
あるcase
ラベルから別のラベルへフォールスルーさせる場合は、必ず[[fallthrough]];
属性でマークしてください。
[[fallthrough]];
should
be placed at a point of execution where a fall-through to the next case label
occurs.
[[fallthrough]];
は次のラベルへのフォールスルーがまさに起こる場所に記述します。
A common exception is consecutive case labels without intervening code,
in which case no annotation is needed.
ただし、間にコードを含まずcase
ラベルが連続している場合には、マークは不要です。
switch (x) { case 41: // No annotation needed here. case 43: if (dont_be_picky) { // Use this instead of or along with annotations in comments. [[fallthrough]]; } else { CloseButNoCigar(); break; } case 42: DoSomethingSpecial(); [[fallthrough]]; default: DoSomethingGeneric(); break; }
switch (x) { case 41: // ここには [[fallthrough]]; は不要です。 case 43: if (dont_be_picky) { // コメントのかわりに(あるいは、コメントとともに)、 // このようにマークします。 [[fallthrough]]; } else { CloseButNoCigar(); break; } case 42: DoSomethingSpecial(); [[fallthrough]]; default: DoSomethingGeneric(); break; }
インクルーシブ・ランゲージ
In all code, including naming and comments, use inclusive language and avoid terms that other programmers might find disrespectful or offensive (such as "master" and "slave", "blacklist" and "whitelist", or "redline"), even if the terms also have an ostensibly neutral meaning. 命名やコメントを含むすべてのコードにおいて「インクルーシブ・ランゲージ」を使用します。 他のプログラマーにとって、無礼または不快に感じられる可能性のある用語 (「マスター」と「スレーブ」、「ブラックリスト」と「ホワイトリスト」、または「レッドライン」など) は、その用語の表面上の意味が中立的なものであったとしても、使用するのを避けてください。 Similarly, use gender-neutral language unless you're referring to a specific person (and using their pronouns). 同様に、ある特定の人(および、その人の代名詞)に言及しようとしている場合を除き、ジェンダーに中立な言葉を使用してください。 For example, use "they"/"them"/"their" for people of unspecified gender (even when singular), and "it"/"its" for software, computers, and other things that aren't people. たとえば、ジェンダーを特定せずに人を指す言葉として(単数の場合でも) "they" / "them" / "their" を使用します。"it" / "its" は、ソフトウェアやコンピューターなどのものに対して使用し、人に対しては用いません。
命名規則
The most important consistency rules are those that govern naming. コードの一貫性を保つためのもっとも重要なルールは、名前の付け方を決めることです。 The style of a name immediately informs us what sort of thing the named entity is: a type, a variable, a function, a constant, a macro, etc., without requiring us to search for the declaration of that entity. 名前のスタイルを適切に定めることで、それが何であるか、すなわち、型なのか、変数なのか、関数なのか、定数なのか、マクロなのか、その宣言を探すことなく、すぐにわかるようにすることができます。 The pattern-matching engine in our brains relies a great deal on these naming rules. 私たちの脳がもつパターンマッチングエンジンは、こうした命名規則に大きく依存しています。
Naming rules are pretty arbitrary, but we feel that consistency is more important than individual preferences in this area, so regardless of whether you find them sensible or not, the rules are the rules. 命名規則はかなり恣意的なものですが、私たちは、この領域において、個人の好みよりも、一貫性が保たれることを重要視しています。 ですので、以下で定める命名規則については、あなたにとってわかりやすいと感じるかどうかに関わらず、「ルールはルール」と考えるようにしてください。
全般的な命名規則
Optimize for readability using names that would be clear even to people on a different team. プロジェクトチーム外のプログラマーから見ても、その意図が明確に伝わるような命名を行い、コードの可読性を最適化してください。
Use names that describe the purpose or intent of the object.
オブジェクトには、そのオブジェクトの目的や意図を説明する名前を付けてください。
Do not worry about saving horizontal space as it is far
more important to make your code immediately
understandable by a new reader.
画面の横幅を節約する必要はありません。そんなことよりも、コードを新しい読者でも直感的に理解できるようにしておくことの方がはるかに重要です。
Minimize the use of
abbreviations that would likely be unknown to someone outside
your project (especially acronyms and initialisms).
プロジェクトチーム外の読者が知らないかもしれない省略語(特に、頭字語やイニシャル化すること)は最小限にとどめてください。
Do not
abbreviate by deleting letters within a word.
また、単語の中の文字を削って省略系を作ってもいけません。
As a rule of thumb, an abbreviation is probably OK if it's listed in
Wikipedia.
経験則的には、Wikipediaに載っているような省略系であれば概ねOKと言えるでしょう。
Generally speaking, descriptiveness should be
proportional to the name's scope of visibility.
一般論として、名前は、その名前が見えるスコープの大きさに比例してその詳細度を決めるようにします。
For example,
n
may be a fine name within a 5-line function,
but within the scope of a class, it's likely too vague.
たとえば、「n
」という名前は、5行しかない関数の中であれば悪くはない命名ですが、クラススコープ変数に同じ命名を行ったとしたら、意味が曖昧すぎるでしょう。
class MyClass { public: int CountFooErrors(const std::vector<Foo>& foos) { int n = 0; // Clear meaning given limited scope and context for (const auto& foo : foos) { ... ++n; } return n; } void DoSomethingImportant() { std::string fqdn = ...; // Well-known abbreviation for Fully Qualified Domain Name } private: const int kMaxAllowedConnections = ...; // Clear meaning within context };
class MyClass { public: int CountFooErrors(const std::vector<Foo>& foos) { int n = 0; // スコープと文脈が限られているため十分明確 for (const auto& foo : foos) { ... ++n; } return n; } void DoSomethingImportant() { std::string fqdn = ...; // 一般的によく使われる、Fully Qualified Domain Name の略語 } private: const int kMaxAllowedConnections = ...; // この文脈において意味が明確 };
class MyClass { public: int CountFooErrors(const std::vector<Foo>& foos) { int total_number_of_foo_errors = 0; // Overly verbose given limited scope and context for (int foo_index = 0; foo_index < foos.size(); ++foo_index) { // Use idiomatic `i` ... ++total_number_of_foo_errors; } return total_number_of_foo_errors; } void DoSomethingImportant() { int cstmr_id = ...; // Deletes internal letters } private: const int kNum = ...; // Unclear meaning within broad scope };
class MyClass { public: int CountFooErrors(const std::vector<Foo>& foos) { int total_number_of_foo_errors = 0; // 十分に狭いスコープと文脈に対して、名前が冗長すぎ for (int foo_index = 0; foo_index < foos.size(); ++foo_index) { // 慣用的`i`を使うべき ... ++total_number_of_foo_errors; } return total_number_of_foo_errors; } void DoSomethingImportant() { int cstmr_id = ...; // 単語の途中の文字が削られていると読みにくい } private: const int kNum = ...; // スコープが広く、何を表すのか不明瞭 };
Note that certain universally-known abbreviations are OK, such as
i
for an iteration variable and T
for a
template parameter.
なお、イテレーション変数のi
、テンプレート引数のT
など、一般に広く知られている短縮形を用いることは問題ありません。
For the purposes of the naming rules below, a "word" is anything that you
would write in English without internal spaces.
これ以降の命名規則のルールにおいて、「単語」とは、英語で書くときにスペースを含まずに綴られるもののことを指すとします。
This includes abbreviations,
such as acronyms and initialisms.
これには、頭字語やイニシャルなどの略語も含みます。
For names written in mixed case (also
sometimes referred to as
"camel case" or
"Pascal case"), in
which the first letter of each word is capitalized, prefer to capitalize
abbreviations as single words, e.g., StartRpc()
rather than
StartRPC()
.
各単語の頭文字を大文字として大文字小文字を混在させる命名(「キャメルケース」または「パスカルケース」と呼ばれるもの)を行う際には、略語も1つの単語であるかのように表記します。例えば StartRPC()
ではなくStartRpc()
とします。
Template parameters should follow the naming style for their category: type template parameters should follow the rules for type names, and non-type template parameters should follow the rules for variable names. テンプレート引数の命名規則は、それぞれの引数が属するカテゴリに従います。 つまり、テンプレート引数が型引数ならば型の命名規則に従い、それ以外の非型引数は変数の命名規則に従います。
ファイル名
Filenames should be all lowercase and can include
underscores (_
) or dashes (-
).
ファイル名はすべて小文字とします。アンダースコア(_
)かダッシュ(-
)を含めてもかまいません。
Follow the convention that your
project uses.
プロジェクトで採用している命名規則に従ってください。
If there is no consistent
local pattern to follow, prefer "_
".
従うべき一貫したルールが定められていない場合には、アンダースコアを使ってください。
Examples of acceptable file names: 例として、次のようなファイル名は問題ありません。
-
my_useful_class.cc
-
my-useful-class.cc
-
myusefulclass.cc
-
myusefulclass_test.cc // _unittest and _regtest are deprecated.
myusefulclass_test.cc // _unittest や _regtest は廃止
C++ files should have a .cc
filename extension, and header files
should have a .h
extension.
ソースファイルの拡張子は.cc
、ヘッダーファイルの拡張子は.h
とします。
Files that rely on being textually included at specific points
should end in
.inc
(see also the section on
self-contained headers).
特定の場所にそのままインクルードされることを意図しているファイルには、拡張子.inc
をつけます(自己完結型ヘッダーのセクションも参照してください)。
Do not use filenames that already exist in
/usr/include
, such as
db.h
.
既に/usr/include
に存在するファイル名(たとえばdb.h
など)をつけてはいけません。
In general, make your filenames very specific.
一般に、ファイル名は非常に具体的な名前となるようにしてください。
For
example, use http_server_logs.h
rather than
logs.h
.
たとえば、単にlogs.h
とはせず、http_server_logs.h
のような命名を行ってください。
A very common case is to have a pair
of files called, e.g.,
foo_bar.h
and
foo_bar.cc
, defining a class called
FooBar
.
非常に良くあるパターンは、ファイルのペアfoo_bar.h
とfoo_bar.cc
でクラスFooBar
を定義するようなやり方です。
型名
Type names start with a capital letter and have a capital
letter for each new word, with no underscores:
MyExcitingClass
,
MyExcitingEnum
.
型の名前は大文字で始め、単語ごとの頭文字を大文字にします。アンダースコアは使いません。たとえば、MyExcitingClass
、MyExcitingEnum
のようにします。
The names of all types — classes, structs, type aliases, enums, and type template parameters — have the same naming convention. 型に類するもの、すなわちクラス、構造体、型のエイリアス、列挙型、型テンプレート引数はすべて、この規則に従って命名します。 Type names should start with a capital letter and have a capital letter for each new word. 型の名前は大文字で始めて、新しい単語ごとに頭文字を大文字にします。 No underscores. アンダースコアは使いません。 For example: 以下に例を示します。
// classes and structs class UrlTable { ... class UrlTableTester { ... struct UrlTableProperties { ... // typedefs typedef hash_map<UrlTableProperties *, std::string> PropertiesMap; // using aliases using PropertiesMap = hash_map<UrlTableProperties *, std::string>; // enums enum class UrlTableError { ...
// クラスと構造体 class UrlTable { ... class UrlTableTester { ... struct UrlTableProperties { ... // typedef typedef hash_map<UrlTableProperties *, std::string> PropertiesMap; // usingエイリアス using PropertiesMap = hash_map<UrlTableProperties *, std::string>; // 列挙型 enum class UrlTableError { ...
コンセプトの名前
Concept names follow the same rules as type names. コンセプトの名前は、型の名前と同じ規則に従います。
変数名
The names of variables (including function parameters) and data members are
snake_case
(all lowercase, with underscores between words).
変数や関数引数、データメンバーの名前には、snake_case
(すべて小文字、単語間はアンダースコア)を使います。
Data members of classes
(but not structs) additionally have trailing underscores.
構造体を除くクラスのデータメンバ名には、末尾に追加のアンダースコアをつけます。
For instance:
a_local_variable
, a_struct_data_member
,
a_class_data_member_
.
たとえば、それぞれ、a_local_variable
、a_struct_data_member
、a_class_data_member_
のようにします。
一般的な変数名
For example: 以下に例を示します。
std::string table_name; // OK - snake_case.
std::string table_name; // OK - snake_caseになっている
std::string tableName; // Bad - mixed case.
std::string tableName; // ダメ - 大文字小文字が混ざっている。
クラスのデータメンバ
Data members of classes, both static and non-static, are
named like ordinary nonmember variables, but with a
trailing underscore.
クラスのデータメンバは、static
の有無にかかわらず、通常の変数と同様の命名を行い、さらに、末尾に追加のアンダースコアをつけます。
class TableInfo { ... private: std::string table_name_; // OK - underscore at end. static Pool<TableInfo>* pool_; // OK. };
class TableInfo { ... private: std::string table_name_; // OK。末尾にアンダースコア static Pool<TableInfo>* pool_; // OK。 };
構造体のデータメンバ
Data members of structs, both static and non-static,
are named like ordinary nonmember variables.
構造体のデータメンバは、static
の有無にかかわらず、通常の変数と同じように名前を付けます。
They do not have
the trailing underscores that data members in classes have.
クラスのデータメンバとは異なり、末尾にアンダースコアはつけません。
struct UrlTableProperties { std::string name; int num_entries; static Pool<UrlTableProperties>* pool; };
See Structs vs. Classes for a discussion of when to use a struct versus a class. 構造体とクラスの使い分けに関する議論については、構造体か、クラスかを参照してください。
定数名
Variables declared constexpr
or const
, and whose value is fixed for
the duration of the program, are named with a leading "k" followed
by mixed case.
constexpr
やconst
で宣言され、かつ、プログラムの最初から最後まで常に不変の固定の値をもつ変数は、頭に「k」をつけた上で、大文字小文字混じりの名前を付けます。
Underscores can be used as separators in the rare cases
where capitalization cannot be used for separation.
単語の境界において大文字化することができない場合は、アンダースコアを区切りとして使ってもかまいません。
For example:
以下に例を示します。
const int kDaysInAWeek = 7; const int kAndroid8_0_0 = 24; // Android 8.0.0
All such variables with static storage duration (i.e., statics and globals, see Storage Duration for details) should be named this way, including those in templates where different instantiations of the template may have different values. このような、値が不変の静的記憶域時間の定数変数(すなわち、静的変数やグローバル変数ですが、詳細はStorage Durationを参照してください)は、この規則に従って命名します。これには、テンプレートの静的変数について、それらの値がテンプレートインスタンスごとに異なる場合も含みます。 This convention is optional for variables of other storage classes, e.g., automatic variables; otherwise the usual variable naming rules apply. それ以外の記憶域期間に分類される定数変数(自動変数など)については、この命名規則に従うか、あるいは、通常の変数と同様の命名規則に従うかのいずれか任意とします。
void ComputeFoo(absl::string_view suffix) { // Either of these is acceptable. const absl::string_view kPrefix = "prefix"; const absl::string_view prefix = "prefix"; ... }
void ComputeFoo(absl::string_view suffix) { // 以下のどちらでもOKです const absl::string_view kPrefix = "prefix"; const absl::string_view prefix = "prefix"; ... }
void ComputeFoo(absl::string_view suffix) { // Bad - different invocations of ComputeFoo give kCombined different values. const std::string kCombined = absl::StrCat(kPrefix, suffix); ... }
void ComputeFoo(absl::string_view suffix) { // これはダメ。kCombined が関数呼び出し毎に異なる値になりうる const std::string kCombined = absl::StrCat(kPrefix, suffix); ... }
関数名
Regular functions have mixed case; accessors and mutators may be named like variables. 通常の関数は、大文字小文字を混ぜて命名します。なお、getter関数やsetter関数は、変数名の命名規則に従ってもかまいません。
Ordinarily, functions should start with a capital letter and have a capital letter for each new word. 通常は、関数名は大文字で始めて、単語ごとに頭文字を大文字にします。
AddTableEntry() DeleteUrl() OpenFileOrDie()
(The same naming rule applies to class- and namespace-scope constants that are exposed as part of an API and that are intended to look like functions, because the fact that they're objects rather than functions is an unimportant implementation detail.) (クラススコープや名前空間スコープにおいて、APIの一部として公開される、関数のように振る舞う定数オブジェクトについても、関数名の命名規則を適用してください。これらが実際のところはオブジェクトであり、関数ではないという事実は、APIのユーザーの視点からはあまり重要ではない実装の詳細であると言えるからです。)
Accessors and mutators (get and set functions) may be named like
variables.
getter関数やsetter関数は、変数のような名前を付けてもかまいません。
These often correspond to actual member variables, but this is
not required.
また、これらの関数は、実在のメンバ変数と紐づけられることが多いですが、必ずしもそうでなくてもかまいません。
For example, int count()
and void
set_count(int count)
.
たとえば、int count()
とvoid set_count(int count)
のようにしてもかまいません。
名前空間の名前
Namespace names are all lower-case, with words separated by underscores. 名前空間の名前は、すべて小文字で、単語間はアンダースコアで区切ります。 Top-level namespace names are based on the project name. トップレベル(最も外側)の名前空間には、プロジェクト名に基づいた名前を付けます。 Avoid collisions between nested namespaces and well-known top-level namespaces. また、ネストされた名前空間であっても、よく知られたトップレベル名前空間の名前と衝突させるのは避けてください。
The name of a top-level namespace should usually be the name of the project or team whose code is contained in that namespace. トップレベル名前空間の名前には、通常はプロジェクト名かチーム名を付けます。 The code in that namespace should usually be in a directory whose basename matches the namespace name (or in subdirectories thereof). 名前空間に含まれるコードは、通常は名前空間の名前と同名のディレクトリ(か、そのサブディレクトリ)に置きます。
Keep in mind that the rule against abbreviated names applies to namespaces just as much as variable names. 名前空間の名前を付けるときにも、変数名と同様に省略系に関するルールが適用されることに注意してください。 Code inside the namespace seldom needs to mention the namespace name, so there's usually no particular need for abbreviation anyway. 名前空間内のコードにおいて、それを囲む名前空間の名前を必要とすることはほとんどありませんので、いずれにせよ、名前空間名に省略系を使う需要はないはずです。
Avoid nested namespaces that match well-known top-level
namespaces.
ネストされた名前空間であっても、よく知られたトップレベル名前空間の名前と同じ名前は付けないでください。
Collisions between namespace names can lead to surprising
build breaks because of name lookup rules.
そのような名前空間名の衝突があると、名前検索のルールによって、想定外のビルドエラーを引き起こすことがあります。
In particular, do not
create any nested std
namespaces.
特に、std
という名前の名前空間は、いかなる階層であっても作ってはいけません。
Prefer unique project
identifiers
(websearch::index
, websearch::index_util
)
over collision-prone names like websearch::util
.
プロジェクト名自体にもなるべくユニークとなる識別子をつけることが望ましいです。
たとえば、websearch::util
のような衝突しやすい名前よりも、websearch::index
や websearch::index_util
のような名前にします。
Also avoid overly deep nesting
namespaces (TotW #130).
また、名前空間のネストを深くしすぎないようにしてください(TotW #130)。
For internal
namespaces, be wary of other code being
added to the same internal
namespace causing a collision
(internal helpers within a team tend to be related and may lead to
collisions).
internal
名前空間においては、同じinternal
名前空間に追加される他のコードと衝突しないように、特に注意が必要です。(チーム内向けのヘルパー同士がこの関係に陥る傾向があり、ときに衝突につながります)。
In such a situation, using the filename to make a unique
internal name is helpful
(websearch::index::frobber_internal
for use
in frobber.h
)
このような場合には、ファイル名を使ってユニークな名前を作る方法が役立ちます(たとえばfrobber.h
では、websearch::index::frobber_internal
のようにします)。
列挙型の名前
Enumerators (for both scoped and unscoped enums) should be named like
constants, not like
macros.
That is, use kEnumName
not
ENUM_NAME
.
列挙型(スコープ付き、スコープなしの両方とも)は、定数の命名規則(kEnumName
)に従います。マクロの命名規則(ENUM_NAME
)は使いません。
enum class UrlTableError { kOk = 0, kOutOfMemory, kMalformedInput, };
enum class AlternateUrlTableError { OK = 0, OUT_OF_MEMORY = 1, MALFORMED_INPUT = 2, };
Until January 2009, the style was to name enum values like macros. 2009年1月までは、列挙値はマクロの命名規則に従っていましたが、 This caused problems with name collisions between enum values and macros. 列挙値とマクロとの間で名前が衝突する問題が起きたため、 Hence, the change to prefer constant-style naming was put in place. 列挙型の名前は定数の命名規則に従うようルールが変更されました。 New code should use constant-style naming. 新しく書くコードでは定数の命名規則に従ってください。
マクロの名前
You're not really going to
define a macro, are you? If you do, they're like this:
MY_MACRO_THAT_SCARES_SMALL_CHILDREN_AND_ADULTS_ALIKE
.
本当はマクロを定義しようだなんて思ってないですよね……?
マクロを定義する場合は、MY_MACRO_THAT_SCARES_SMALL_CHILDREN_AND_ADULTS_ALIKE
((訳注:「小さな子供も大人も同じように怖がらせる私のマクロ」の意)) のように名前をつけます。
Please see the description of macros; in general macros should not be used. まず、マクロの詳細を確認してください。通常、マクロを使うべきではありません。 However, if they are absolutely needed, then they should be named with all capitals and underscores, and with a project-specific prefix. それでも、どうしてもマクロが必要な場合は、すべてを大文字にし、アンダースコアで単語を区切り、さらにプロジェクト固有の接頭辞をつけて命名してください。
#define MYPROJECT_ROUND(x) ...
命名規則の例外
If you are naming something that is analogous to an existing C or C++ entity then you can follow the existing naming convention scheme. 名前をつけようとしている対象がC言語やC++における既存のエンティティと類似している場合は、それらに倣った命名規則に従ってもかまいません。
bigopen()
-
function name, follows form of
open()
open()
の形に倣った関数名 uint
typedef
bigpos
-
struct
orclass
, follows form ofpos
pos
の形に倣ったstruct
やclass
sparse_hash_map
- STL-like entity; follows STL naming conventions STLの命名規則に倣ったSTL風エンティティ
LONGLONG_MAX
-
a constant, as in
INT_MAX
INT_MAX
に倣った定数
コメント
Comments are absolutely vital to keeping our code readable. コードの可読性を保つためには、コメントが絶対に必要不可欠です。 The following rules describe what you should comment and where. 以下のルールでは、どこに・どのようなコメントを残すべきか説明します。 But remember: while comments are very important, the best code is self-documenting. ただし、確かにコメントは非常に重要で、それは間違いありませんが、それよりも最もベストなのは、コード自身が自分で自分の意味を説明できていることです。 Giving sensible names to types and variables is much better than using obscure names that you must then explain through comments. 型や変数に明確で伝わりやすい名前をつけることは、曖昧な命名をしてコメントで説明するよりも、はるかに優れています。
When writing your comments, write for your audience: the next contributor who will need to understand your code. コメントを書くときは、その読者に宛てるつもりで書いてください。 つまり、そのコードを次に担当するメンバーが、そのコードを理解できるようになるために、コメントを書いてください。 Be generous — the next one may be you! ひょっとすると、次のコードの担当者はあなた自身かもしれません。コメントは惜しみなく書きましょう。
((訳注: この日本語翻訳版ガイドではコード例の中のコメントも日本語に訳してありますが、本ガイドのルールはあくまで英語のコメントを対象としており、本翻訳版も英語コメントを対象としたもののまま訳していますので注意してください。とはいえ、これらのコメントの書き方のルールについては、日本語でコメントを書く場合でも大いに適用できる部分があると訳者は考えます。))
コメントのスタイル
Use either the //
or /* */
syntax, as long as you are consistent.
コメントは、一貫性が保たれている限り、//
か/* */
のどちらの文法を使ってもかまいません。
You can use either the //
or the /*
*/
syntax; however, //
is
much more common.
コメントは、//
と/* */
のどちらの文法で書いてもかまいません。ただし、//
を用いる方がはるかに一般的です。
Be consistent with how you
comment and what style you use where.
どのようにコメントを書くか、どのスタイルに従うかについて、一貫性を保ってください。
ファイルに対するコメント
Start each file with license boilerplate. 各ファイルは、ライセンスに関する定型文で始めてください。
If a source file (such as a .h
file) declares multiple user-facing abstractions
(common functions, related classes, etc.), include a comment describing the collection of those
abstractions.
あるソースファイル(.h
ファイル等)において、公開APIとしての抽象(共通関数や関連クラス等)を複数宣言している場合には、それら全体を総括的に説明するコメントを含めてください。
Include enough detail for future authors to know what does not fit there.
また、コードの将来のメンテナーがそれを読んだとき、何をそこに含めるのがふさわしく、何がふさわしくないのか判断できるような、十分な説明を含めてください。
However,
the detailed documentation about individual abstractions belongs with those abstractions, not at the
file level.
なお、そのファイルに含まれる個々の抽象に関する詳細な情報は、ファイルレベルのコメントではなく、それぞれの抽象に属するようにしてください。
For instance, if you write a file comment for frobber.h
, you do not need
to include a file comment in frobber.cc
or
frobber_test.cc
.
例として、frobber.h
にファイルコメントを書くのであれば、frobber.cc
やfrobber_test.cc
にはファイルコメントを書く必要はありません。
On the other hand, if you write a collection of classes in
registered_objects.cc
that has no associated header file, you must include a file
comment in registered_objects.cc
.
反対に、たとえば、registered_objects.cc
にクラスのコレクションを書いていて、そのファイルと対応するヘッダーファイルが存在しないのであれば、registered_objects.cc
にファイルレベルのコメントを含めるようにしてください。
法的通知と著者に関する行
Every file should contain license boilerplate. すべてのファイルに、ライセンスに関する定型文を含めてください。 Choose the appropriate boilerplate for the license used by the project (for example, Apache 2.0, BSD, LGPL, GPL). その際、そのプロジェクトが採用するライセンス(たとえば、Apache 2.0, BSD, LGPL, GPL等)に応じて、適切な定型文を選んでください。
If you make significant changes to a file with an author line, consider deleting the author line. あるファイルを大きく変更した場合は、そのファイルの著者に関する行(存在する場合)を削除することを検討してください。 New files should usually not contain copyright notice or author line. 新しく作るファイルについては、通常は、著作権表記や著者の行は含めません。
構造体やクラスに対するコメント
Every non-obvious class or struct declaration should have an accompanying comment that describes what it is for and how it should be used. 極めて単純なものを除いて、クラスや構造体の宣言には、その型の目的と使い方に関するコメントを書いてください。
// Iterates over the contents of a GargantuanTable. // Example: // std::unique_ptr<GargantuanTableIterator> iter = table->NewIterator(); // for (iter->Seek("foo"); !iter->done(); iter->Next()) { // process(iter->key(), iter->value()); // } class GargantuanTableIterator { ... };
// GargantuanTable全体をイテレートします // 使用例: // std::unique_ptr<GargantuanTableIterator> iter = table->NewIterator(); // for (iter->Seek("foo"); !iter->done(); iter->Next()) { // process(iter->key(), iter->value()); // } class GargantuanTableIterator { ... };((訳注: Gargantuan: 途方もないくらい大きな))
クラスに対するコメント
The class comment should provide the reader with enough information to know how and when to use the class, as well as any additional considerations necessary to correctly use the class. クラスのコメントには、そのクラスをいつ・どのように用いれば良いかがわかるように、また、そのクラスを正しく扱うための付加的な考慮事項がある場合はそれらに関しても、十分な情報を含めるようにしてください。 Document the synchronization assumptions the class makes, if any. クラスが、同期に関する前提条件を持つ場合には、それらについても記述してください。 If an instance of the class can be accessed by multiple threads, take extra care to document the rules and invariants surrounding multithreaded use. クラスのインスタンスが複数のスレッドからのアクセスを受け入れるのであれば、マルチスレッド下でそれらを扱う際のルールや不変条件に関して、細心の注意を払って記述してください。
The class comment is often a good place for a small example code snippet demonstrating a simple and focused usage of the class. クラスのコメントは、そのクラスの使用例などの小さなコードスニペットを記述するのにも都合の良い場所です。
When sufficiently separated (e.g.,
.h
and
.cc
files), comments describing the use of the class should go together with its
interface definition; comments about the class operation and implementation
should accompany the implementation of the class's methods.
コードが.h
と.cc
ファイルとに十分に分離されているときは、クラスの使い方に関する内容をインターフェース定義のコメントに記述し、クラスが行う操作や実装に関する内容をメソッドの実装のコメントに記述してください。
関数に対するコメント
Declaration comments describe use of the function (when it is non-obvious); comments at the definition of a function describe operation. 関数宣言のコメントでは、その関数の使い方について (それが、明白でない場合) 説明してください。関数定義のコメントでは、関数によって行われる操作について説明してください。
関数宣言時のコメント
Almost every function declaration should have comments immediately
preceding it that describe what the function does and how to use
it.
ほとんどすべての関数宣言において、その宣言の直前に、関数の目的 (その関数が何をするのか) とその使用方法に関するコメントが必要です。
These comments may be omitted only if the function is simple and
obvious (e.g., simple accessors for obvious properties of the class).
関数が非常にシンプルで明らかな場合はコメントを省略してもかまいません。
たとえば、クラスのデータメンバに対するgetter関数などではコメントを書く必要はありません。
Private methods and functions declared in .cc
files are not exempt.
なお、プライベートメソッドや、.cc
内で宣言された関数だからといって、コメントが免除されるわけではありません。
Function comments should be written with an implied subject of
This function and should start with the verb phrase; for example,
"Opens the file", rather than "Open the file".
関数コメントは、This function(この関数は)を暗黙的な主語として、動詞句から始めます。
たとえば、"Opens the file(ファイルを開きます)"です。"Open the file(ファイルを開け)" ではありません((訳注: 英文法の三単現の`s`のことを言っています))。
In general, these comments do not
describe how the function performs its task.
一般に、これらの関数宣言時のコメントでは、どのように処理が行われるかについては触れません。
Instead, that should be
left to comments in the function definition.
そのような内容は、関数宣言時ではなく、関数定義時のコメントに譲ってください。
Types of things to mention in comments at the function declaration: 関数宣言時のコメントで言及すべきことは以下のような内容です。
-
What the inputs and outputs are.
関数の入力と出力は何か。
If function argument names
are provided in `backticks`, then code-indexing
tools may be able to present the documentation better.
関数の引数名は
`backticks`
のようにバッククオートで囲んで記述すると、コードインデックスツールによってドキュメント化した場合の表示がよくなるかもしれません。 - For class member functions: whether the object remembers reference or pointer arguments beyond the duration of the method call. クラスメンバ関数の引数における参照やポインタについて、関数から戻った後もそれらの参照を内部に保持し続けるのか否か。 This is quite common for pointer/reference arguments to constructors. 特にコンストラクタの引数にポインタや参照を渡すパターンは非常にありふれています。
-
For each pointer argument, whether it is allowed to be null and what happens
if it is.
各ポインタ引数について、その引数が
nullptr
であってもよいか否か。また、nullptr
であった場合にどのような挙動をするのか。 - For each output or input/output argument, what happens to any state that argument is in. (E.g. is the state appended to or overwritten?). 各出力用引数や各入出力用引数において、関数呼び出し前の各引数の状態に対して、呼び出し後の状態がどうなるか(例えば、出力用引数に変数を渡したとき、そこに値が追加されるのか、あるいは、単に上書きされるのか)。
- If there are any performance implications of how a function is used. 関数の使い方によってパフォーマンスに影響がある場合は、その情報。
Here is an example: 以下に例を示します。
// Returns an iterator for this table, positioned at the first entry // lexically greater than or equal to `start_word`. If there is no // such entry, returns a null pointer. The client must not use the // iterator after the underlying GargantuanTable has been destroyed. // // This method is equivalent to: // std::unique_ptr<Iterator> iter = table->NewIterator(); // iter->Seek(start_word); // return iter; std::unique_ptr<Iterator> GetIterator(absl::string_view start_word) const;
// テーブル内で`start_word`に等しいか辞書的に次に大きい最初の要素を指す // イテレータを返します。そのような要素がない場合、nullptrが返ります。 // 対象のGargantuanTableが破壊された後は、この関数から戻ったイテレータを // 使用してはいけません。 // // このメソッドは次の呼び出しと等価です: // std::unique_ptr<Iterator> iter = table->NewIterator(); // iter->Seek(start_word); // return iter; std::unique_ptr<Iterator> GetIterator(absl::string_view start_word) const;
However, do not be unnecessarily verbose or state the completely obvious. ただし、コメントを無用に冗長にしたり、完全に明白なことをわざわざ主張したりはしないでください。
When documenting function overrides, focus on the specifics of the override itself, rather than repeating the comment from the overridden function. オーバーライドした関数にドキュメントを書くときは、オーバーライドする前の関数と同等のコメントを繰り返すのではなく、そのオーバーライド自体の内容にフォーカスしたコメントを記述してください。 In many of these cases, the override needs no additional documentation and thus no comment is required. ただし、多くの場合において、関数のオーバーライド時に追加の情報を必要とすることはありませんので、そのような場合には、あえてコメントする必要はありません。
When commenting constructors and destructors, remember that the person reading your code knows what constructors and destructors are for, so comments that just say something like "destroys this object" are not useful. コンストラクタやデストラクタにコメントを書くときは、コードの読者は、コンストラクタとは何か、デストラクタとは何か、既に知っているということに注意してください。「オブジェクトを破壊します」というコメントには何の情報もありません。 Document what constructors do with their arguments (for example, if they take ownership of pointers), and what cleanup the destructor does. コンストラクタのコメントでは、コンストラクタが与えられた引数をどう扱うのか (たとえば、ポインタの所有権を引き取るか否か) について説明してください。デストラクタのコメントでは、デストラクタが何をクリーンアップするかについて記述します。 If this is trivial, just skip the comment. わざわざ説明するまでもなければ、コメントを省略してかまいません。 It is quite common for destructors not to have a header comment. 実際、デストラクタのヘッダーコメントは省略されるのが極めて一般的です。
関数定義時のコメント
If there is anything tricky about how a function does its job, the function definition should have an explanatory comment. 関数がその処理において何かしらトリッキーなことをしている場合には、関数定義のコメントで、それらについて説明してください。 For example, in the definition comment you might describe any coding tricks you use, give an overview of the steps you go through, or explain why you chose to implement the function in the way you did rather than using a viable alternative. 関数定義のコメントでは、たとえば、関数の実装で使われているコーディングトリックを説明したり、関数の処理手順の概要を示したり、あるいは、様々な実装方法の中からその方法を選んだ理由を説明したりすることができます。 For instance, you might mention why it must acquire a lock for the first half of the function but why it is not needed for the second half. 具体的な例として、たとえば、関数前半ではロックの取得を必要とするが、関数後半では不要となる理屈について説明することもできるでしょう。
Note you should
not just repeat the comments
given with the function declaration, in the
.h
file or wherever.
なお、ヘッダーファイル等で行った関数宣言時のコメントを繰り返すだけのコメントは避けてください。
It's okay to
recapitulate briefly what the function does, but the
focus of the comments should be on how it does it.
関数の目的を軽く繰り返す程度はかまいませんが、関数定義時のコメントでは、どちらかといえば、どのようにそれを実現するのかについてフォーカスしたコメントを記述してください。
変数に対するコメント
In general the actual name of the variable should be descriptive enough to give a good idea of what the variable is used for. 一般に、変数の名前には、その変数が何のために使われているか、その目的がわかるような、十分に説明的な名前をつけているはずです。 In certain cases, more comments are required. それでも、さらなるコメントを必要とするケースがありますので、ここではそれについて説明します。
クラスデータメンバに対するコメント
The purpose of each class data member (also called an instance
variable or member variable) must be clear.
クラスの各データメンバ(メンバ変数)の目的は明確でなければなりません。
If there are any
invariants (special values, relationships between members, lifetime
requirements) not clearly expressed by the type and name, they must be
commented.
変数の型や変数名だけでは明確に表現できない不変条件(たとえば、特殊な意味を持つ値の存在や、メンバ間の関連性、寿命に関する要件など)がある場合は、それらをコメントとして記述しなければなりません。
However, if the type and name suffice (int
num_events_;
), no comment is needed.
反対に、int num_events_;
のように、型と名前だけで十分説明できているのであれば、コメントは不要です。
In particular, add comments to describe the existence and meaning
of sentinel values, such as nullptr or -1, when they are not
obvious.
特に、nullptr
や-1
のような値に、番兵などの特殊な意味を持たせている場合で、そのことが明白でないときは、そのような値の存在や意味を説明するコメントを付け足してください。
For example:
以下に例を示します。
private: // Used to bounds-check table accesses. -1 means // that we don't yet know how many entries the table has. int num_total_entries_;
private: // テーブルアクセス時の境界チェックに使います。-1 は、 // まだテーブルにいくつの要素があるかわからないことを意味します。 int num_total_entries_;
グローバル変数に対するコメント
All global variables should have a comment describing what they are, what they are used for, and (if unclear) why they need to be global. すべてのグローバル変数は、その変数が何を表していて、何のために使われ、そしてなぜグローバルである必要があるのか (明確でない場合) 説明するコメントが必要です。 For example: 以下に例を示します。
// The total number of test cases that we run through in this regression test. const int kNumTestCases = 6;
// この回帰テストにおけるテストケースの総数 const int kNumTestCases = 6;
実装に対するコメント
In your implementation you should have comments in tricky, non-obvious, interesting, or important parts of your code. 実装のコードの中では、トリッキーな部分や、不明瞭な部分、興味深い部分、重要な部分について、コメントを記述してください。
解説のコメント
Tricky or complicated code blocks should have comments before them. トリッキーあるいは複雑なコードブロックについては、その手前に、それらに関するコメントを記述してください。
関数実引数のコメント
When the meaning of a function argument is nonobvious, consider one of the following remedies: 関数呼び出し時、実引数の意味を捉えにくい場合は、次に述べる対策のいずれかを検討してください。
- If the argument is a literal constant, and the same constant is used in multiple function calls in a way that tacitly assumes they're the same, you should use a named constant to make that constraint explicit, and to guarantee that it holds. 引数が定数リテラルで、かつ、複数の関数呼び出しの間で、同じ定数値を同じ意味で使用しているような場合には、名前付きの定数を導入して、それらに関する制約を明示的にし、また、常に同じ値であることを保証するようにしてください。
-
Consider changing the function signature to replace a
bool
argument with anenum
argument. 関数シグネチャにおけるbool
型引数はenum
に置き換えることを検討してください。 This will make the argument values self-describing.enum
を使うと、関数呼び出し時に実引数として現れる値そのものが説明的になります。 - For functions that have several configuration options, consider defining a single class or struct to hold all the options , and pass an instance of that. 関数がいくつかの設定オプションを持つような場合には、すべてのオプションをまとめて保持する構造体かクラスを定義し、そのインスタンスを関数に渡す方法を検討してください。 This approach has several advantages. この方法にはいくつか利点があります。 Options are referenced by name at the call site, which clarifies their meaning. まず、呼び出し元のコードにおいて、各オプションが名前によって参照されるようになるため、値の意味が明確になります。 It also reduces function argument count, which makes function calls easier to read and write. また、関数引数の数が減るため、関数呼び出しそのものが読みやすく書きやすくなります。 As an added benefit, you don't have to change call sites when you add another option. 追加の利点として、将来的に関数のオプションを増やすときにも、呼び出し側のコードを書き換える必要がなくなります。
- Replace large or complex nested expressions with named variables. 長い、あるいは、複雑なネストを含む式は、名前付きの変数で置き換えてください。
- As a last resort, use comments to clarify argument meanings at the call site. 最後の手段として、関数呼び出し元で、引数の意味を明確にするためのコメントを記述してください。
// What are these arguments? const DecimalNumber product = CalculateProduct(values, 7, false, nullptr);
// これらの引数の意味は何? const DecimalNumber product = CalculateProduct(values, 7, false, nullptr);
versus: これに対して、次の例はどうでしょうか。
ProductOptions options; options.set_precision_decimals(7); options.set_use_cache(ProductOptions::kDontUseCache); const DecimalNumber product = CalculateProduct(values, options, /*completion_callback=*/nullptr);
ProductOptions options; options.set_precision_decimals(7); options.set_use_cache(ProductOptions::kDontUseCache); const DecimalNumber product = CalculateProduct(values, options, /*completion_callback=*/nullptr);
してはならないこと
Do not state the obvious. 明白なことをわざわざ主張してはいけません。 In particular, don't literally describe what code does, unless the behavior is nonobvious to a reader who understands C++ well. C++を十分理解している読者にとってもわかりにくい挙動をするような場合は除きますが、それ以外の場合、特に、コードがしていることを逐一文字通りに説明するようなコメントを書いてはいけません。 Instead, provide higher level comments that describe why the code does what it does, or make the code self describing. そのようなコメントのかわりに、より高い次元の、たとえばそのコードがなぜそれをしているのかをコメントで記述したり、あるいはコード自身にそれを説明させるようにしてください。
Compare this: 以下のコード例を比べてみましょう。// Find the element in the vector. <-- Bad: obvious! if (std::find(v.begin(), v.end(), element) != v.end()) { Process(element); }
// vectorの中から要素を検索する。 <-- ダメ。見ればわかる。 if (std::find(v.begin(), v.end(), element) != v.end()) { Process(element); }
// Process "element" unless it was already processed. if (std::find(v.begin(), v.end(), element) != v.end()) { Process(element); }
// まだ処理していない要素があれば処理する。 auto iter = std::find(v.begin(), v.end(), element); if (iter != v.end()) { Process(element); }
if (!IsAlreadyProcessed(element)) { Process(element); }
句読点と綴りと文法
Pay attention to punctuation, spelling, and grammar; it is easier to read well-written comments than badly written ones. コメントを書く際には、句読点の使い方、正しい綴り((訳注:日本語の場合は誤変換も))、正しい文法を使うことに注意を払ってください。きちんとしたコメントはそれだけで読みやすいものです。
Comments should be as readable as narrative text, with proper capitalization and punctuation. コメントは、大文字や句読点を適切に用いてふつうの文章として読めるように記述します。 In many cases, complete sentences are more readable than sentence fragments. 多くの場合において、文の断片だけの場合よりも、完全な文にする方が読みやすくなります。 Shorter comments, such as comments at the end of a line of code, can sometimes be less formal, but you should be consistent with your style. 行末コメントなどの短いコメントでは多少形式を崩すこともありますが、極力、一貫したスタイルを保つようにはしてください。
Although it can be frustrating to have a code reviewer point out that you are using a comma when you should be using a semicolon, it is very important that source code maintain a high level of clarity and readability. コードレビュアーからの指摘のうち「セミコロンを使うべきところでカンマを使っている」といったものにはうんざりするかもしれません。しかし、ソースコードの明瞭性や可読性を高いレベルに保つことは、本当に重要なことなのです。 Proper punctuation, spelling, and grammar help with that goal. 適切な句読点、正しい綴り、正しい文法は、このゴールに向かうための手助けをしてくれるでしょう。
TODOコメント
Use
TODO
comments for code that is temporary,
a short-term solution, or good-enough but not perfect.
一時的なコード、暫定対応コード、完璧ではないが現状十分なコード等に対しては、TODO
コメントを記述してください。
TODO
s should include the string
TODO
in all caps, followed by the
bug ID, name, e-mail address, or other
identifier
of the person or issue with the best context
about the problem referenced by the
TODO
.
TODO
コメントは、まず、すべて大文字でTODO
と書き、続けて、バグ管理番号や、そのTODO
に関して、詳しい人の名前やメールアドレスやその他のID等、あるいは、関連するissueへのリンク等を含めます。
// TODO: bug 12345678 - Remove this after the 2047q4 compatibility window expires. // TODO: example.com/my-design-doc - Manually fix up this code the next time it's touched. // TODO(bug 12345678): Update this list after the Foo service is turned down. // TODO(John): Use a "\*" here for concatenation operator.
// TODO: bug 12345678 - 2047q4の互換性維持期間がおわったら消す // TODO: example.com/my-design-doc - 次回ここを触るときには直す // TODO(bug 12345678): Fooサービスを止めたらこのリストを更新する // TODO(John): ここでは連結演算子として"\*"を使う
If your TODO
is of the form "At a future
date do something" make sure that you either include a
very specific date ("Fix by November 2005") or a very
specific event ("Remove this code when all clients can
handle XML responses.").
もし、そのTODO
が「未来のいつ、何をする」という形式になるときは、必ず、具体的な期日(例:「2005年11月までに直す」)や、具体的なイベント(例:「すべてのクライアントがXMLレスポンスを扱えるようになったらこのコードは消す」)を含めるようにしてください。
コードのフォーマット
Coding style and formatting are pretty arbitrary, but a project is much easier to follow if everyone uses the same style. コーディングスタイルやコードフォーマットは好みによるところが大きいものですが、全員が同じスタイルに沿っていれば、プロジェクト全体にも同じルールを適用しやすくなります。 Individuals may not agree with every aspect of the formatting rules, and some of the rules may take some getting used to, but it is important that all project contributors follow the style rules so that they can all read and understand everyone's code easily. ここで定めるフォーマットのルールの中には、各個人にとって賛同しかねるものもあれば、非常に馴染み深いものもあるでしょう。それでも、ここでは、すべてのプロジェクトメンバーが、ある一つのスタイルに沿うことを重要視します。プロジェクト全体でルールを統一することによって、誰もが誰のコードでも読みやすく理解しやすい状態を保つことができるのです。
To help you format code correctly, we've created a settings file for emacs. コードを正しくフォーマットするために、emacsの設定ファイルを用意しています。
行の長さ
Each line of text in your code should be at most 80 characters long. コードの各行の長さは、最大80文字までとします。
We recognize that this rule is controversial, but so much existing code already adheres to it, and we feel that consistency is important. このルールに議論の余地があることは認識していますが、既に多くのコードがこのルールに沿って書かれており、そして、私たちは一貫性が保たれることを重視しています。
Those who favor this rule argue that it is rude to force them to resize their windows and there is no need for anything longer. このルールを推す側の主張は、長いコードによって読者にウィンドウサイズの変更を強制するのは失礼であるし、そもそもコードを横に長くする必要はない、というものです。 Some folks are used to having several code windows side-by-side, and thus don't have room to widen their windows in any case. 人によっては複数のコードウィンドウを横に並べて使っていて、それ以上ウィンドウ幅を広げる余地がないかもしれません。 People set up their work environment assumeing a particular maximum window width, and 80 columns has been the traditional standard. 多くの人が、特定の最大ウィンドウサイズの想定のもとに仕事環境を構築しており、その際の伝統的な標準として横幅80文字が使われてきました。 Why change it? 変える理由があるでしょうか?
Proponents of change argue that a wider line can make code more readable. このルールを変えたがっている側の主張は、行を長くすることでコードがもっと読みやすくなるはずである、というものです。 The 80-column limit is an hidebound throwback to 1960s mainframes; modern equipment has wide screens that can easily show longer lines. 横幅80文字という制限は、1960年代のメインフレームを思い起こさせる干からびたものです。現代的な環境は、もっと長い行を簡単に表示できる広いスクリーンを備えているではありませんか?
80 characters is the maximum. 最大80文字とします。
A line may exceed 80 characters if it is 次に該当する場合は、80文字を超えてもかまいません。
- a comment line which is not feasible to split without harming readability, ease of cut and paste or auto-linking -- e.g., if a line contains an example command or a literal URL longer than 80 characters. 行を分割すると可読性や利便性の妨げとなるようなコメント行。 たとえば、80文字を超えるような長いコマンド例や長いURLがこれに該当します。このような行を分割してしまうと、可読性を損ねたり、コピー&ペーストしにくくなったり、URLの自動リンクが正しく機能しなくなったりしてしまいます。
- a string literal that cannot easily be wrapped at 80 columns. 80文字での折り返しが難しい長い文字列リテラル。 This may be because it contains URIs or other semantically-critical pieces, or because the literal contains an embedded language, or a multiline literal whose newlines are significant like help messages. これには、URIやその他の意味的に重要なものや、別の組み込み言語を含む文字列リテラル、あるいはヘルプメッセージのような、改行が重要な意味を持つ複数行文字列リテラルなどが該当します。 In these cases, breaking up the literal would reduce readability, searchability, ability to click links, etc. このような場合においてリテラルを分割してしまうと、可読性や検索性を損なったり、リンクがクリックできなくなったりします。 Except for test code, such literals should appear at namespace scope near the top of a file. なお、テスト用コードを除いて、このようなリテラルは、ファイル先頭付近の名前空間スコープ内におくとよいでしょう。 If a tool like Clang-Format doesn't recognize the unsplittable content, disable the tool around the content as necessary. また、もしClang-Formatのようなツールが、そのようなコンテンツを正しく認識しないときは、必要に応じて、そのコンテンツの周りでツールを無効化してください。 (We must balance between usability/searchability of such literals and the readability of the code around them.) (なお、このようなリテラルに対する利便性や検索性と、その周辺のコードの読みやすさとのバランスも保つようにしてください)
-
an include statement.
#include
文。 - a header guard インクルードガード。
- a using-declaration using宣言。
非アスキー文字
Non-ASCII characters should be rare, and must use UTF-8 formatting. 基本的に非アスキー文字は使いません。もし、非アスキー文字を使う場合はUTF-8フォーマットを使います。
You shouldn't hard-code user-facing text in source, even English, so use of non-ASCII characters should be rare. まず前提として、そもそもユーザーの目に触れるようなテキストをソースコード中にハードコーディングしてはいけません。たとえ、英語であってもです。ですから、非アスキー文字をソースコード中で使う機会はほとんどないはずです。 However, in certain cases it is appropriate to include such words in your code. しかし、場合によっては、このような単語をソースコードに含めることが適切であるようなケースもあります。 For example, if your code parses data files from foreign sources, it may be appropriate to hard-code the non-ASCII string(s) used in those data files as delimiters. たとえば、海外から来る何らかのデータファイルを解析するためのコードにおいて、そのデータの区切り文字として使われている非アスキー文字列をソースコードに埋め込むのは妥当なことでしょう。 More commonly, unittest code (which does not need to be localized) might contain non-ASCII strings. もっと一般的な例として、(ローカライズの必要がない)ユニットテストにおいて、非アスキー文字列を含むこともあるでしょう。 In such cases, you should use UTF-8, since that is an encoding understood by most tools able to handle more than just ASCII. このような場合には、エンコーディングとしてUTF-8を使います。UTF-8であれば、ASCII以外も解するほとんどのツールでサポートされています。
Hex encoding is also OK, and encouraged where it
enhances readability — for example,
"\xEF\xBB\xBF"
, or, even more simply,
"\uFEFF"
, is the Unicode zero-width
no-break space character, which would be invisible
if included in the source as straight UTF-8.
16進数によるエンコーディングを使ってもかまいません。特に、そうすることで可読性が高まる場合は積極的に使用してください。たとえば、"\xEF\xBB\xBF"
や、より単純に"\uFEFF"
は、Unicodeのゼロ幅改行なしスペース文字ですが、これが普通のUTF-8文字としてソースコード中に埋め込まれていたら、ソースコードの中では見えなくなってしまうでしょう。
When possible, avoid the u8
prefix.
可能な限り、u8
接頭辞を避けてください。
It has significantly different semantics starting in C++20
than in C++17, producing arrays of char8_t
rather than char
, and will change again in C++23.
C++17以前と比べて、C++20で、そのセマンティクスが大きく変更され、u8
接頭辞は、char
の配列ではなくchar8_t
の配列を生成するようになりました。そして、さらなる変更がC++23で予定されています。
You shouldn't use char16_t
and
char32_t
character types, since they're for
non-UTF-8 text.
char16_t
とchar32_t
は、UTF-8以外のテキストのためのものであるため、原則として使うことはありません。
For similar reasons you also shouldn't
use
wchar_t
(unless you're writing code that
interacts with the Windows API, which uses
wchar_t
extensively).
同様の理由で、 (Windows APIを使うコードを除き) wchar_t
も使いません。
スペースか、タブか
Use only spaces, and indent 2 spaces at a time. スペースだけを使います。インデント1つにつき、スペース2つとします。
We use spaces for indentation. インデントにはスペースを使います。 Do not use tabs in your code. タブは使いません。 You should set your editor to emit spaces when you hit the tab key. タブキーを押したときに、スペースが入力されるようにエディタを設定してください。
関数宣言と関数定義
Return type on the same line as function name, parameters on the same line if they fit. 関数の戻り値の型は、関数名と同じ行に記述します。 引数は、行内に収まる限り、同じ行に記述します。 Wrap parameter lists which do not fit on a single line as you would wrap arguments in a function call. 引数リストが1行に収まらないときは、関数呼び出しと同じ方法で、行を分割します。
Functions look like this: 関数は次のようにします。
ReturnType ClassName::FunctionName(Type par_name1, Type par_name2) { DoSomething(); ... }
If you have too much text to fit on one line: 1行に収まらないときは、次のようにします。
ReturnType ClassName::ReallyLongFunctionName(Type par_name1, Type par_name2, Type par_name3) { DoSomething(); ... }
or if you cannot fit even the first parameter: もし、最初の引数からして同じ行に収まらないのであれば、次のようにします。
ReturnType LongClassName::ReallyReallyReallyLongFunctionName( Type par_name1, // 4 space indent Type par_name2, Type par_name3) { DoSomething(); // 2 space indent ... }
ReturnType LongClassName::ReallyReallyReallyLongFunctionName( Type par_name1, // スペース4個でインデント Type par_name2, Type par_name3) { DoSomething(); // スペース2個でインデント ... }
Some points to note: 次の点に注意してください。
- Choose good parameter names. 引数には良い名前を付けてください。
- A parameter name may be omitted only if the parameter is not used in the function's definition. 関数定義において、関数本体で使わない引数は、その名前を省略してもかまいません。
- If you cannot fit the return type and the function name on a single line, break between them. 戻り値の型と関数名とが同じ行に収まらないときは、それらの間で行を分割してください。
- If you break after the return type of a function declaration or definition, do not indent. 戻り値の型の後で改行する場合、後続の関数名の行をインデントしないでください。
-
The open parenthesis is always on the same line as
the function name.
関数の開き丸括弧
(
は常に関数名と同じ行に書きます。 -
There is never a space between the function name
and the open parenthesis.
関数名と開き丸括弧
(
の間にはスペースを入れてはいけません。 -
There is never a space between the parentheses and
the parameters.
開き丸括弧
(
と最初の引数、最後の引数と閉じ丸括弧)
の間にはスペースを入れてはいけません。 -
The open curly brace is always on the end of the last line of the function
declaration, not the start of the next line.
開き波括弧
{
は常に関数宣言の最後の行に書きます。次の行の頭ではありません。 -
The close curly brace is either on the last line by
itself or on the same line as the open curly brace.
閉じ波括弧
}
は、それ自身で最後の行とするか、開き波括弧{
と同じ行に書くかのいずれかとします。 -
There should be a space between the close
parenthesis and the open curly brace.
閉じ丸括弧
)
と開き波括弧{
の間にはスペースを1つ入れます。 - All parameters should be aligned if possible. 可能な限り、すべての引数を整列させます。
- Default indentation is 2 spaces. デフォルトのインデントはスペース2つです。
- Wrapped parameters have a 4 space indent. 引数リストの先頭で行を分割した場合は、スペース4つでインデントします。
Unused parameters that are obvious from context may be omitted: 次の例のように、引数が使われず、かつ、その意味が文脈から明白な場合は、引数を省略できます。
class Foo { public: Foo(const Foo&) = delete; Foo& operator=(const Foo&) = delete; };
Unused parameters that might not be obvious should comment out the variable name in the function definition: ただし、使わない引数であっても、その意味が文脈から明白でないときには、省略せずにコメントとして残します。
class Shape { public: virtual void Rotate(double radians) = 0; }; class Circle : public Shape { public: void Rotate(double radians) override; }; void Circle::Rotate(double /*radians*/) {}
// Bad - if someone wants to implement later, it's not clear what the // variable means. void Circle::Rotate(double) {}
// これはダメ。将来、実装を追加するとき、引数の意図がわからなくなってしまう。 void Circle::Rotate(double) {}
Attributes, and macros that expand to attributes, appear at the very beginning of the function declaration or definition, before the return type: 属性や、属性に展開されるマクロは、関数宣言や関数定義の最も先頭、戻り値の型よりも前に書きます。
ABSL_ATTRIBUTE_NOINLINE void ExpensiveFunction(); [[nodiscard]] bool IsOk();
ラムダ式
Format parameters and bodies as for any other function, and capture lists like other comma-separated lists. ラムダ式の引数リストと関数本体は、通常の関数と同様にフォーマットします。 キャプチャリストは、他のカンマ区切りのリストと同様にフォーマットします。
For by-reference captures, do not leave a space between the
ampersand (&
) and the variable name.
参照キャプチャについて、アンパサンド(&
)と変数名の間には、スペースを入れません。
int x = 0; auto x_plus_n = [&x](int n) -> int { return x + n; }
Short lambdas may be written inline as function arguments. 短いラムダは、関数の実引数として直接その場に書いてもかまいません。
absl::flat_hash_set<int> to_remove = {7, 8, 9}; std::vector<int> digits = {3, 9, 1, 8, 4, 7, 1}; digits.erase(std::remove_if(digits.begin(), digits.end(), [&to_remove](int i) { return to_remove.contains(i); }), digits.end());
浮動小数点リテラル
Floating-point literals should always have a radix point, with digits on both
sides, even if they use exponential notation.
浮動小数点リテラルでは、指数表記を使用する場合であっても、常に小数点を書き、常に小数点の両側に1桁以上ずつ数字を書きます。
Readability is improved if all
floating-point literals take this familiar form, as this helps ensure that they
are not mistaken for integer literals, and that the
E
/e
of the exponential notation is not mistaken for a
hexadecimal digit.
すべての浮動小数点リテラルの見た目が、この見慣れた形に統一されていれば、それらを整数リテラルと取り違えたり、指数表記のE
やe
を16進数に見間違えたりすることもなくなり、コードの可読性の向上に繋がります。
It is fine to initialize a floating-point variable with an
integer literal (assuming the variable type can exactly represent that integer),
but note that a number in exponential notation is never an integer literal.
浮動小数点変数を整数リテラルで初期化してもかまいません (ただし、その整数値を正確に表せる場合に限ります)。一方で、指数表記された数値は決して整数リテラルにはなりませんので注意してください。
float f = 1.f; long double ld = -.5L; double d = 1248e6;
float f = 1.0f; float f2 = 1.0; // Also OK float f3 = 1; // Also OK long double ld = -0.5L; double d = 1248.0e6;
float f = 1.0f; float f2 = 1.0; // これもOK float f3 = 1; // これもOK long double ld = -0.5L; double d = 1248.0e6;
関数呼び出し
Either write the call all on a single line, wrap the
arguments at the parenthesis, or start the arguments on a new
line indented by four spaces and continue at that 4 space
indent.
関数呼び出しは、全体を1行で書くか、引数リストを途中改行して丸括弧(
のところで整列させるか、4つのスペースでインデントした新しい行から引数を書きはじめるかのいずれかとします。
In the absence of other considerations, use the
minimum number of lines, including placing multiple arguments
on each line where appropriate.
他に特別な理由がない限り、各行に複数の引数をまとめて、可能な限り少ない行数で記述します。
Function calls have the following format: 関数呼び出しは、次のォーマットに従います。
bool result = DoSomething(argument1, argument2, argument3);
If the arguments do not all fit on one line, they
should be broken up onto multiple lines, with each
subsequent line aligned with the first argument.
もし、すべての引数が1行に収まらない場合、それらを複数の行に分割します。その際、後続の行は、最初の引数に揃えるようにします。
Do not
add spaces after the open paren or before the close
paren:
開き丸括弧(
の後や、閉じ丸括弧)
の前にスペースを入れてはいけません。
bool result = DoSomething(averyveryveryverylongargument1, argument2, argument3);
Arguments may optionally all be placed on subsequent lines with a four space indent: 引数は、それらをすべて、スペース4つでインデントされた後続の行に記述してもかまいません。
if (...) { ... ... if (...) { bool result = DoSomething( argument1, argument2, // 4 space indent argument3, argument4); ... }
if (...) { ... ... if (...) { bool result = DoSomething( argument1, argument2, // スペース4つでインデント argument3, argument4); ... }
Put multiple arguments on a single line to reduce the number of lines necessary for calling a function unless there is a specific readability problem. 可読性の妨げとならない限り、複数の引数を1つの行にまとめ、関数呼び出しに要する行数を減らしてください。 Some find that formatting with strictly one argument on each line is more readable and simplifies editing of the arguments. 人によっては、厳密に1行に1つずつ引数を置く形式の方が読みやすく、編集もしやすいと考えるかもしれません。 However, we prioritize for the reader over the ease of editing arguments, and most readability problems are better addressed with the following techniques. しかし、私たちは、編集のしやすよりも、読者にとっての読みやすさを優先します。また、読みやすさに関する問題のほとんどは、以下で述べるテクニックによって解決することができます。
If having multiple arguments in a single line decreases readability due to the complexity or confusing nature of the expressions that make up some arguments, try creating variables that capture those arguments in a descriptive name: 引数が複雑な式から構成されていて、1行にそれらをまとめると読みにくくなるという場合には、次のように、それらの引数のための一時変数を作って、適切な名前を与えてください。
int my_heuristic = scores[x] * y + bases[x]; bool result = DoSomething(my_heuristic, x, y, z);
Or put the confusing argument on its own line with an explanatory comment: あるいは、次のように、意味のわかりにくい引数を個別の行にわけて、説明のためのコメントを記述してください。
bool result = DoSomething(scores[x] * y + bases[x], // Score heuristic. x, y, z);
bool result = DoSomething(scores[x] * y + bases[x], // ヒューリスティックなスコア x, y, z);
If there is still a case where one argument is significantly more readable on its own line, then put it on its own line. それでもまだ、引数を1行に1つだけにした方がはるかに読みやすいという場合には、いよいよそのようにしましょう。 The decision should be specific to the argument which is made more readable rather than a general policy. 全体的なポリシーを守るよりも、それらの引数が読みやすくなる方を選んでください。
Sometimes arguments form a structure that is important for readability. 時折、関数引数は、次の例のように、可読性の面で重要な、何らかの構造を成すことがあります。 In those cases, feel free to format the arguments according to that structure: このような場合には、その構造に沿って自由に引数を配置してかまいません。
// Transform the widget by a 3x3 matrix. my_widget.Transform(x1, x2, x3, y1, y2, y3, z1, z2, z3);
// ウィジェットを3x3行列で変形する my_widget.Transform(x1, x2, x3, y1, y2, y3, z1, z2, z3);
波括弧による初期化子リスト
Format a braced initializer list exactly like you would format a function call in its place. 波括弧による初期化子リストは、関数呼び出しと同じようにフォーマットします。
If the braced list follows a name (e.g., a type or
variable name), format as if the
{}
were the
parentheses of a function call with that name.
波括弧のリストが型名や変数名の直後に続く場合は、それらの波括弧{}
を関数呼び出しのときの丸括弧()
に見立てて、その引数と同じようにフォーマットします。
If there
is no name, assume a zero-length name.
名前がない場合は、0文字の名前があると考えてフォーマットします。
// Examples of braced init list on a single line. return {foo, bar}; functioncall({foo, bar}); std::pair<int, int> p{foo, bar}; // When you have to wrap. SomeFunction( {"assume a zero-length name before {"}, some_other_function_parameter); SomeType variable{ some, other, values, {"assume a zero-length name before {"}, SomeOtherType{ "Very long string requiring the surrounding breaks.", some, other, values}, SomeOtherType{"Slightly shorter string", some, other, values}}; SomeType variable{ "This is too long to fit all in one line"}; MyType m = { // Here, you could also break before {. superlongvariablename1, superlongvariablename2, {short, interior, list}, {interiorwrappinglist, interiorwrappinglist2}};
// 1行の初期化子リストの例 return {foo, bar}; functioncall({foo, bar}); std::pair<int, int> p{foo, bar}; // 複数行にする場合 SomeFunction( {"{の前に、0文字の名前があるかのように扱います"}, some_other_function_parameter); SomeType variable{ some, other, values, {"{の前に、0文字の名前があるかのように扱います"}, SomeOtherType{ "それの前後に改行を要するようなとてもとても長い文字列。", some, other, values}, SomeOtherType{"それよりは少し短めの文字列", some, other, values}}; SomeType variable{ "全体を1行に納めるには少しばかり長すぎる文字列"}; MyType m = { // {の前で改行してもかまいません superlongvariablename1, // 超長い変数名 superlongvariablename2, {short, interior, list}, // 短いリスト {interiorwrappinglist, // 改行を要するリスト interiorwrappinglist2}};
ループと条件分岐
At a high level, looping or branching statements consist of the following components: おおまかに、ループや分岐のための文は、次のような部品から成り立っていると言えます。
- One or more statement keywords (e.g.
if
,else
,switch
,while
,do
, orfor
). 1つ以上のキーワード(statement keywords)(if
,else
,switch
,while
,do
,for
) - One condition or iteration specifier, inside parentheses.括弧で囲まれた、1つの条件指定子(condition specifier)もしくはイテレーション指定子(iteration specifier)
- One or more controlled statements, or blocks of controlled statements.1つ以上の制御対象文(controlled statements)もしくはそのブロック
For these statements:これらの文について:
- The components of the statement should be separated by single spaces (not line breaks). 文を構成する部品同士の間は、改行せず1つのスペースで区切ります。
-
Inside the condition or iteration specifier, put one space (or a line
break) between each semicolon and the next token, except if the token is a
closing parenthesis or another semicolon.
条件指定子やイテレーション指定子の内側において、各セミコロン
;
と続くトークンの間は1つのスペース(か改行)で区切ります。ただし、セミコロン;
の次のトークンが閉じ丸括弧)
あるいはセミコロン;
であるときはスペース(か改行)を省きます。 -
Inside the condition or iteration specifier, do not put a space after the
opening parenthesis or before the closing parenthesis.
条件指定子やイテレーション指定子の内側において、開き丸括弧
(
の後や閉じ丸括弧)
の前にはスペースを入れません。 -
Put any controlled statements inside blocks (i.e. use curly braces).
すべての制御対象文はブロックに入れます(つまり、常に波括弧
{
...}
で囲みます)。 -
Inside the controlled blocks, put one line break immediately after the
opening brace, and one line break immediately before the closing brace.
制御対象文のブロックの内側では、開き波括弧
{
の後で1回改行し、閉じ波括弧}
の前で1回改行します。
if (condition) { // Good - no spaces inside parentheses, space before brace. DoOneThing(); // Good - two-space indent. DoAnotherThing(); } else if (int a = f(); a != 3) { // Good - closing brace on new line, else on same line. DoAThirdThing(a); } else { DoNothing(); } // Good - the same rules apply to loops. while (condition) { RepeatAThing(); } // Good - the same rules apply to loops. do { RepeatAThing(); } while (condition); // Good - the same rules apply to loops. for (int i = 0; i < 10; ++i) { RepeatAThing(); }
if (condition) { // 良い例。丸括弧内はスペースなし、開き波括弧の前にスペースあり DoOneThing(); // 良い例。スペース2つでインデント DoAnotherThing(); } else if (int a = f(); a != 3) { // 良い例。閉じ波括弧の前で改行、elseは同一行内 DoAThirdThing(a); } else { DoNothing(); } // 良い例。ループ文にも同じルールを適用します。 while (condition) { RepeatAThing(); } // 良い例。ループ文にも同じルールを適用します。 do { RepeatAThing(); } while (condition); // 良い例。ループ文にも同じルールを適用します。 for (int i = 0; i < 10; ++i) { RepeatAThing(); }
if(condition) {} // Bad - space missing after `if`. else if ( condition ) {} // Bad - space between the parentheses and the condition. else if (condition){} // Bad - space missing before `{`. else if(condition){} // Bad - multiple spaces missing. for (int a = f();a == 10) {} // Bad - space missing after the semicolon. // Bad - `if ... else` statement does not have braces everywhere. if (condition) foo; else { bar; } // Bad - `if` statement too long to omit braces. if (condition) // Comment DoSomething(); // Bad - `if` statement too long to omit braces. if (condition1 && condition2) DoSomething();
if(condition) {} // ダメ。ifの後にスペースがない。 else if ( condition ) {} // ダメ。括弧と条件の間にスペースがある。 else if (condition){} // ダメ。{の前にスペースがない。 else if(condition){} // ダメ。諸々スペースがない for (int a = f();a == 10) {} // ダメ。セミコロンの後にスペースがない。 // ダメ。`if ... else`文のすべての場所に{}がない if (condition) foo; else { bar; } // ダメ。{}を省略するにはif文が長すぎる if (condition) // 何かしらコメント何かしらコメント何かしらコメント DoSomething(); // ダメ。{}を省略するにはif文が長すぎる if (condition1 && condition2) DoSomething();
For historical reasons, we allow one exception to the above rules: the curly
braces for the controlled statement or the line breaks inside the curly braces
may be omitted if as a result the entire statement appears on either a single
line (in which case there is a space between the closing parenthesis and the
controlled statement) or on two lines (in which case there is a line break
after the closing parenthesis and there are no braces).
歴史的経緯により、このルールについては、文全体が1行か2行におさまる場合の例外を設けています。この条件に該当する場合は、制御対象文を囲む波括弧自体や、あるいは、波括弧の内側の改行を省略しても許容するものとします。
全体を1行とする場合は、条件の閉じ括弧)
と制御対象文との間に1つスペースをあけます。
全体を2行とする場合は、条件の閉じ括弧)
の直後で改行し、波括弧を削ります。
// OK - fits on one line. if (x == kFoo) { return new Foo(); } // OK - braces are optional in this case. if (x == kFoo) return new Foo(); // OK - condition fits on one line, body fits on another. if (x == kBar) Bar(arg1, arg2, arg3);
// 許容される例。1行におさまっている。 if (x == kFoo) { return new Foo(); } // 許容される例。このケースでは{}を省略できます。 if (x == kFoo) return new Foo(); // 許容される例。条件が1行に、制御対象文が次の1行におさまっている。 if (x == kBar) Bar(arg1, arg2, arg3);
This exception does not apply to multi-keyword statements like
if ... else
or do ... while
.
ただし、この例外ルールは、if...else
や、do...while
等の、複数のキーワードからなる構文には適用しません。これらの場合は常にすべての波括弧が必要です。
// Bad - `if ... else` statement is missing braces. if (x) DoThis(); else DoThat(); // Bad - `do ... while` statement is missing braces. do DoThis(); while (x);
// ダメ。`if ... else`の形においては{}は省略不可 if (x) DoThis(); else DoThat(); // ダメ。`do ... while`の形においては{}は省略不可 do DoThis(); while (x);
Use this style only when the statement is brief, and consider that loops and branching statements with complex conditions or controlled statements may be more readable with curly braces. この例外的なスタイルは、文が短く簡潔である場合にのみ適用してください。 ループ文や条件分岐文が、複雑な条件あるいは複雑な制御対象文を持つときは、通常通り波括弧を用いて読みやすくできないか検討してください。 Some projects require curly braces always. また、プロジェクトによっては、この例外ルールを認めず、常に波括弧を必須とするよう定めていることもありますので注意してください。
case
blocks in switch
statements can have curly
braces or not, depending on your preference.
switch
文のcase
ブロックでは、波括弧を書いても書かなくてもかまいません。
If you do include curly braces,
they should be placed as shown below.
もし、波括弧を書く場合は、次のようにしてください。
switch (var) { case 0: { // 2 space indent Foo(); // 4 space indent break; } default: { Bar(); } }
switch (var) { case 0: { // スペース2つでインデント Foo(); // スペース4つでインデント break; } default: { Bar(); } }
Empty loop bodies should use either an empty pair of braces or
continue
with no braces, rather than a single semicolon.
ループの本体が空になる場合は、波括弧による空のブロックとするか、波括弧なしでcontinue
を使います。セミコロンだけを書いてはいけません。
while (condition) {} // Good - `{}` indicates no logic. while (condition) { // Comments are okay, too } while (condition) continue; // Good - `continue` indicates no logic.
while (condition) {} // 良い例。`{}`によって、ロジックがないことを明示している。 while (condition) { // コメントだけを書いてもよい } while (condition) continue; // 良い例。 `continue` によって、ロジックがないことを明示している。
while (condition); // Bad - looks like part of `do-while` loop.
while (condition); // ダメ。do-whileループの一部にも見える。
ポインタと参照の表現
No spaces around period or arrow. ドット演算子やアロー演算子の周りにスペースは使いません。 Pointer operators do not have trailing spaces. ポインタ演算子の後にはスペースを使いません。
The following are examples of correctly-formatted pointer and reference expressions: ポインタおよび参照に関する正しいフォーマットの例を以下に示します。
x = *p; p = &x; x = r.y; x = r->y;
Note that: 以下のことに注意してください。
-
There are no spaces around the period or arrow when
accessing a member.
メンバにアクセスするためのドット演算子(
.
)やアロー演算子(->
)の周りにはスペースを置きません。 -
Pointer operators have no space after the
*
or&
. ポインタ演算子(*
や&
)の後にはスペースを置きません。
When referring to a pointer or reference (variable declarations or definitions, arguments,
return types, template parameters, etc), you may place the space before or after the
asterisk/ampersand.
ポインタ型や参照型の宣言 (変数、引数、戻り値型、テンプレート引数など) では、アスタリスク(*
)やアンパサンド(&
)を型名と変数名のどちらにつけてもかまいません。
In the trailing-space style, the space is elided in some cases (template
parameters, etc).
型名につける (後ろにスペースを置く) スタイルの場合、テンプレート引数などの型名のみを要する箇所ではスペースを省きます。
// These are fine, space preceding. char *c; const std::string &str; int *GetPointer(); std::vector<char *> // These are fine, space following (or elided). char* c; const std::string& str; int* GetPointer(); std::vector<char*> // Note no space between '*' and '>'
// これらはOK。スペースが先。 char *c; const std::string &str; int *GetPointer(); std::vector<char *> // これらもOK。スペースが後(か省略)。 char* c; const std::string& str; int* GetPointer(); std::vector<char*> // '*' と '>'の間にスペースなし
You should do this consistently within a single file. 1つのファイル内では一貫したスタイルを保つようにしてください。 When modifying an existing file, use the style in that file. 既存のファイルを変更するときは、そのファイル内で使われているスタイルにあわせてください。
It is allowed (if unusual) to declare multiple variables in the same declaration, but it is disallowed if any of those have pointer or reference decorations. 変数宣言において、通常変数は、1つの宣言で複数の変数をまとめて宣言することは問題ありません。しかし、その宣言の中に1つでもポインタや参照の宣言を含む場合は同時に宣言してはいけません。 Such declarations are easily misread. そのような宣言は簡単に誤解してしまうためです。// Fine if helpful for readability. int x, y;
// 可読性が高まる場合はOK int x, y;
int x, *y; // Disallowed - no & or * in multiple declaration char * c; // Bad - spaces on both sides of * const std::string & str; // Bad - spaces on both sides of &
int x, *y; // 不可。&や*が複数宣言の中に含められている。 char * c; // ダメ。*の前後両方にスペースがある。 const std::string & str; // ダメ。&の前後両方にスペースがある。
ブール式
When you have a boolean expression that is longer than the standard line length, be consistent in how you break up the lines. ブール式が標準の行の長さを超えるような場合、行の区切り方について一貫性を保ってください。
In this example, the logical AND operator is always at
the end of the lines:
たとえば、次のコード例では、論理積演算子(&&
)を常に行末に配置しています。
if (this_one_thing > this_other_thing && a_third_thing == a_fourth_thing && yet_another && last_one) { ... }
if (this_one_thing > this_other_thing && a_third_thing == a_fourth_thing && yet_another && last_one) { ... }
Note that when the code wraps in this example, both of
the
&&
logical AND operators are at
the end of the line.
この例において、両方の論理積演算子(&&
)が、いずれも行末にあることに注目してください。
This is more common in Google code,
though wrapping all operators at the beginning of the
line is also allowed.
Googleのコードにおいては、このように演算子を行末に置く形がよく使われますが、演算子を次行の頭に置くスタイルでもかまいません。
Feel free to insert extra
parentheses judiciously because they can be very helpful
in increasing readability when used
appropriately, but be careful about overuse.
また、これらの式において、追加の丸括弧を適切に挿入することで、大幅に可読性を高められる場合があります。そのような丸括弧は自由に挿入してかまいません。ただし、使いすぎには気をつけてください。
Also note that you should always use
the punctuation operators, such as
&&
and ~
, rather than
the word operators, such as and
and
compl
.
なお、これらの論理演算を行う際は、and
やcompl
のような単語による演算子ではなく、常に&&
や~
のような記号による演算子を使用してください。
戻り値
Do not needlessly surround the return
expression with parentheses.
return
文の式を不要な丸括弧で囲んではいけません。
Use parentheses in
return expr;
only
where you would use them in
x = expr;
.
return expr;
に丸括弧を使うのは、式がx = expr;
の形をしている場合のみです。
return result; // No parentheses in the simple case. // Parentheses OK to make a complex expression more readable. return (some_long_condition && another_condition);
return result; // 単純なものには丸括弧を使いません。 // これはOK。複雑な式を読みやすくするために、丸括弧を使っています。 return (some_long_condition && another_condition);
return (value); // You wouldn't write var = (value); return(result); // return is not a function!
return (value); // var = (value);とは書かないでしょう。 return(result); // returnは関数ではありません!
変数と配列の初期化
You may choose between
=
,
()
, and
{}
; the following are
all correct:
=
と()
と{}
のうち、いずれを使用してもかまいません。次の例はすべてOKです。
int x = 3; int x(3); int x{3}; std::string name = "Some Name"; std::string name("Some Name"); std::string name{"Some Name"};
int x = 3; int x(3); int x{3}; std::string name = "Some Name"; std::string name("Some Name"); std::string name{"Some Name"};
Be careful when using a braced initialization list
{...}
on a type with an
std::initializer_list
constructor.
ただし、波括弧による初期化子リスト{...}
については、型がstd::initializer_list
を引数にとるコンストラクタを持つ場合に注意が必要です。
A nonempty
braced-init-list prefers the
std::initializer_list
constructor whenever
possible.
空でない波括弧初期化リストは、可能な限りstd::initializer_list
のコンストラクタを呼び出そうとします。
Note that empty braces {}
are special, and
will call a default constructor if available.
ただし、リストが空{}
のときだけは特別で、デフォルトコンストラクタが優先的に呼び出されます。
To force the
non-std::initializer_list
constructor, use parentheses
instead of braces.
std::initializer_list
以外のコンストラクタを呼び出すことを強制するためには、波括弧{...}
ではなく丸括弧(...)
による初期化を行ってください。
std::vector<int> v(100, 1); // A vector containing 100 items: All 1s. std::vector<int> v{100, 1}; // A vector containing 2 items: 100 and 1.
std::vector<int> v(100, 1); // 中身は「1」が100個。 std::vector<int> v{100, 1}; // 中身は「100」と「1」の2個。
Also, the brace form prevents narrowing of integral types. また、整数型の初期化に波括弧を用いると、小さい型への変換を防ぐことができます。 This can prevent some types of programming errors. これによって、ある種のプログラミング上のミスを防ぐことができます。
int pi(3.14); // OK -- pi == 3. int pi{3.14}; // Compile error: narrowing conversion.
int pi(3.14); // OK? コンパイルが通る。 pi == 3 になる。 int pi{3.14}; // コンパイルエラー: より小さい型への変換
プリプロセッサディレクティブ
The hash mark that starts a preprocessor directive should
always be at the beginning of the line.
プリプロセッサディレクティブのための#
は、常に行頭に置きます。
Even when preprocessor directives are within the body of indented code, the directives should start at the beginning of the line. たとえプリプロセッサディレクティブをインデントされているコードの中に書く場合であっても、ディレクティブは行の頭から記述します。
// Good - directives at beginning of line if (lopsided_score) { #if DISASTER_PENDING // Correct -- Starts at beginning of line DropEverything(); # if NOTIFY // OK but not required -- Spaces after # NotifyClient(); # endif #endif BackToNormal(); }
// OK。プリプロセッサが行の頭にある。 if (lopsided_score) { #if DISASTER_PENDING // 正しい。行頭から始める。 DropEverything(); # if NOTIFY // #の後ならスペースもOK(必須ではない)。 NotifyClient(); # endif #endif BackToNormal(); }
// Bad - indented directives if (lopsided_score) { #if DISASTER_PENDING // Wrong! The "#if" should be at beginning of line DropEverything(); #endif // Wrong! Do not indent "#endif" BackToNormal(); }
// ダメ。ディレクティブがインデントされている。 if (lopsided_score) { #if DISASTER_PENDING // 間違い! "#if"は行頭になくてはならない。 DropEverything(); #endif // 間違い! #endifをインデントしてはダメ。 BackToNormal(); }
クラスのフォーマット
Sections in public
, protected
and
private
order, each indented one space.
クラス内のセクションはpublic
、protected
、private
の順で並べ、これらのキーワードはスペース1文字でインデントします。
The basic format for a class definition (lacking the comments, seeClass Comments for a discussion of what comments are needed) is: クラス定義の基本的なフォーマットを以下に示します(ただし、コメントは省いています。コメントについては、クラスのコメントで議論しています)
class MyClass : public OtherClass { public: // Note the 1 space indent! MyClass(); // Regular 2 space indent. explicit MyClass(int var); ~MyClass() {} void SomeFunction(); void SomeFunctionThatDoesNothing() { } void set_some_var(int var) { some_var_ = var; } int some_var() const { return some_var_; } private: bool SomeInternalFunction(); int some_var_; int some_other_var_; };
class MyClass : public OtherClass { public: // 注意: スペース1つでインデント! MyClass(); // いつもの。スペース2つでインデント explicit MyClass(int var); ~MyClass() {} void SomeFunction(); void SomeFunctionThatDoesNothing() { } void set_some_var(int var) { some_var_ = var; } int some_var() const { return some_var_; } private: bool SomeInternalFunction(); int some_var_; int some_other_var_; };
Things to note: 次の点に注意してください。
- Any base class name should be on the same line as the subclass name, subject to the 80-column limit. 基底クラス名は、できるだけ派生クラス名と同じ行に配置します。ただし、行の長さの制限(80文字)には従います。
-
The
public:
,protected:
, andprivate:
keywords should be indented one space.public:
とprotected:
とprivate:
の各キーワードは、スペース1つでインデントします。 - Except for the first instance, these keywords should be preceded by a blank line. これらのキーワードのうち最初に現れるものを除いて、各キーワードの前に1行の空行を挟みます。 This rule is optional in small classes. ただし、小さなクラスにおいては、空行は任意とします。
- Do not leave a blank line after these keywords. これらのキーワードの後には空行を入れてはいけません。
-
The
public
section should be first, followed by theprotected
and finally theprivate
section.public:
セクションを最初に置き、次にprotected:
、最後にprivate:
の順にします。 - SeeDeclaration Order for rules on ordering declarations within each of these sections. また、各セクション内での宣言は、宣言の順序に従って並べてください。
コンストラクタの初期化子リスト
Constructor initializer lists can be all on one line or with subsequent lines indented four spaces. コンストラクタの初期化子リストは、すべて同じ行に収めるか、あるいは、1行に収まらない場合は、続く行をスペース4つでインデントします。
The acceptable formats for initializer lists are: 初期化子リストの正しい例を以下に示します。
// When everything fits on one line: MyClass::MyClass(int var) : some_var_(var) { DoSomething(); } // If the signature and initializer list are not all on one line, // you must wrap before the colon and indent 4 spaces: MyClass::MyClass(int var) : some_var_(var), some_other_var_(var + 1) { DoSomething(); } // When the list spans multiple lines, put each member on its own line // and align them: MyClass::MyClass(int var) : some_var_(var), // 4 space indent some_other_var_(var + 1) { // lined up DoSomething(); } // As with any other code block, the close curly can be on the same // line as the open curly, if it fits. MyClass::MyClass(int var) : some_var_(var) {}
// 1行にすべて収まるとき MyClass::MyClass(int var) : some_var_(var) { DoSomething(); } // 関数シグネチャと初期化子リストの全体が1行に収まらない場合は、 // コロンの前で行を区切り、スペース4つでインデントします。 MyClass::MyClass(int var) : some_var_(var), some_other_var_(var + 1) { DoSomething(); } // 初期化子リストが複数行にまたがる場合は、 // 1行に1つずつメンバ初期化子を書き、整列させます。 MyClass::MyClass(int var) : some_var_(var), // スペース4つでインデント。 some_other_var_(var + 1) { // 整列させます DoSomething(); } // 他のコードブロックと同様ですが、1行に収まるのであれば、 // 開き波括弧`{`と、閉じ波括弧`}`を、同じ行においてもかまいません。 MyClass::MyClass(int var) : some_var_(var) {}
名前空間のフォーマット
The contents of namespaces are not indented. 名前空間の内容はインデントしません。
Namespaces do not add an extra level of indentation. 名前空間ではインデントを足しません。 For example, use: たとえば、次のようにします。
namespace { void foo() { // Correct. No extra indentation within namespace. ... } } // namespace
namespace { void foo() { // 正しい。名前空間はインデントを追加しない。 ... } } // namespace
Do not indent within a namespace: 名前空間の中ではインデントしてはいけません。
namespace { // Wrong! Indented when it should not be. void foo() { ... } } // namespace
namespace { // 間違い! インデントしてはだめ。 void foo() { ... } } // namespace
When declaring nested namespaces, put each namespace on its own line. 名前空間の宣言をネストするときは、1行に1つずつにします。
namespace foo { namespace bar {
namespace foo { namespace bar {
水平方向の空白
Use of horizontal whitespace depends on location. 水平方向の空白は場所に応じて使いわけます。 Never put trailing whitespace at the end of a line. 行末に空白をおいてはいけません。
全般
int i = 0; // Two spaces before end-of-line comments. void f(bool b) { // Open braces should always have a space before them. ... int i = 0; // Semicolons usually have no space before them. // Spaces inside braces for braced-init-list are optional. If you use them, // put them on both sides! int x[] = { 0 }; int x[] = {0}; // Spaces around the colon in inheritance and initializer lists. class Foo : public Bar { public: // For inline function implementations, put spaces between the braces // and the implementation itself. Foo(int b) : Bar(), baz_(b) {} // No spaces inside empty braces. void Reset() { baz_ = 0; } // Spaces separating braces from implementation. ...
int i = 0; // 行末コメントの前にはスペース2つ。 void f(bool b) { // 開き波括弧{の前には常にスペースを置く。 ... int i = 0; // 通常は、セミコロンの前にスペースを置かない。 // 波括弧初期化子リスト{}の内側のスペースは任意。 // ただし、スペースを置く場合は、必ず左右の両方に置くこと。 int x[] = { 0 }; int x[] = {0}; // 継承や初期化子リストのコロンの前後にはスペースを置く。 class Foo : public Bar { public: // インライン関数の実装において、波括弧と実装の間にはスペースを置く。 Foo(int b) : Bar(), baz_(b) {} // 空の実装にはスペースを置かない。 void Reset() { baz_ = 0; } // 波括弧と実装との間にはスペースを置く。 ...
Adding trailing whitespace can cause extra work for others editing the same file, when they merge, as can removing existing trailing whitespace. 行末にスペースを追加してしまうと、同じファイルで作業している他の人がファイルをマージする際に、これらのスペースを取り除く余計な仕事を発生させてしまいます。 So: Don't introduce trailing whitespace. ですから、行末にスペースを入れてはいけません。 Remove it if you're already changing that line, or do it in a separate clean-up operation (preferably when no-one else is working on the file). 既存の行末のスペースは、そのような行を編集したタイミングで取り除くか、あるいは、別途、コードを整理するタイミングを設けて、まとめて取り除くようにしてください (この場合は、そのファイルを誰も編集していないときに行うのがよいでしょう)。
ループと条件式
if (b) { // Space after the keyword in conditions and loops. } else { // Spaces around else. } while (test) {} // There is usually no space inside parentheses. switch (i) { for (int i = 0; i < 5; ++i) { // Loops and conditions may have spaces inside parentheses, but this // is rare. Be consistent. switch ( i ) { if ( test ) { for ( int i = 0; i < 5; ++i ) { // For loops always have a space after the semicolon. They may have a space // before the semicolon, but this is rare. for ( ; i < 5 ; ++i) { ... // Range-based for loops always have a space before and after the colon. for (auto x : counts) { ... } switch (i) { case 1: // No space before colon in a switch case. ... case 2: break; // Use a space after a colon if there's code after it.
if (b) { // 条件文キーワードやループキーワードは直後にスペースを置きます。 } else { // elseは前後にスペースを置きます。 } while (test) {} // 通常、丸括弧の内側にはスペースを置きません。 switch (i) { for (int i = 0; i < 5; ++i) { // ループと条件式の丸括弧の内側には、スペースを置いてもかまいませんが、 // そのようなケースはレアケースです。周りとの一貫性を保ってください。 switch ( i ) { if ( test ) { for ( int i = 0; i < 5; ++i ) { // ループでは、常にセミコロンの後にスペースを置きます。 // セミコロンの前にもスペースを置いてもかまいませんが、レアケースです。 for ( ; i < 5 ; ++i) { ... // 範囲for文(range-based for)では、常にコロンの前後にスペースを置きます。 for (auto x : counts) { ... } switch (i) { case 1: // caseのコロンの前にはスペースを置きません。 ... case 2: break; // コロンの後にコードが続く場合は、その前にスペースを置きます。
演算子
// Assignment operators always have spaces around them. x = 0; // Other binary operators usually have spaces around them, but it's // OK to remove spaces around factors. Parentheses should have no // internal padding. v = w * x + y / z; v = w*x + y/z; v = w * (x + z); // No spaces separating unary operators and their arguments. x = -5; ++x; if (x && !y) ...
// 代入演算子の前後には常にスペースを置きます。 x = 0; // その他の二項演算子の前後には、通常はスペースを置きます。 // ただし項の前後のスペースは、適宜削除してもかまいません。 // 丸括弧を使う場合は、その内側にはスペースを置きません。 v = w * x + y / z; v = w*x + y/z; v = w * (x + z); // 単項演算子においては、その引数との間にスペースは置きません。 x = -5; ++x; if (x && !y) ...
テンプレートとキャスト
// No spaces inside the angle brackets (< and >), before // <, or between >( in a cast std::vector<std::string> x; y = static_cast<char*>(x); // Spaces between type and pointer are OK, but be consistent. std::vector<char *> x;
// 山括弧(<
と>
)の内側にはスペースを置きません。 // キャストにおける<
の前や、 //>
と(
との間にもスペースを置きません。 std::vector<std::string> x; y = static_cast<char*>(x); // ポインタと型名の間のスペースはOKですが、周囲との一貫性を保ってください。 std::vector<char *> x;
垂直方向の空白
Minimize use of vertical whitespace. 垂直方向の空白は最小限にします。
This is more a principle than a rule: don't use blank lines when you don't have to. これはルールというよりは原則になりますが、コード内に不要な空行を残してはいけません。 In particular, don't put more than one or two blank lines between functions, resist starting functions with a blank line, don't end functions with a blank line, and be sparing with your use of blank lines. 特に、関数と関数との間の空行は多くとも2行までとし、関数を空行で始めたり終わらせたりせず、関数の中でも空行を使いすぎないようにしましょう。 A blank line within a block of code serves like a paragraph break in prose: visually separating two thoughts. コードブロック中の空行には、通常の散文における段落を区切るような、2つの思考や概念を視覚的に分離する効果があります。
The basic principle is: The more code that fits on one screen, the easier it is to follow and understand the control flow of the program. 基本的な原則は「1画面に収まるコードが多ければ多いほど、プログラムの流れが追いやすく、理解しやすくなる」というものです。 Use whitespace purposefully to provide separation in that flow. 空白は、フローを分離する目的をもって、意図的に使いましょう。
Some rules of thumb to help when blank lines may be useful: 空行が有効に働くのはどのような場合か、経験則として、次のルールを参考にしてみてください。
- Blank lines at the beginning or end of a function do not help readability. 関数の最初や最後に空行を入れても、コードが読みやすくなることはありません。
- Blank lines inside a chain of if-else blocks may well help readability. if-elseブロックが連なっているとき、その中に空行を入れると、読みやすくなることがあります。
- A blank line before a comment line usually helps readability — the introduction of a new comment suggests the start of a new thought, and the blank line makes it clear that the comment goes with the following thing instead of the preceding. 通常、コメント行の前に空行をおくと、可読性が向上します。 新しいコメントが入るということは、そこから新しい事柄が始まるということでもあります。 コメントの前に空行を入れることによって、それまでのコードと新しいコメントとが分離され、視覚的に、そのコメントがその後に続く事柄に関するものであることを表現できます。
- Blank lines immediately inside a declaration of a namespace or block of namespaces may help readability by visually separating the load-bearing content from the (largely non-semantic) organizational wrapper. 名前空間の宣言 (あるいは、その固まり) のすぐ内側に空行を置くと、ファイルの主となるコンテンツ部分と、ほとんど定型文であるファイルを系統立てるためのラッパー部分とを視覚的に分離することができるため、コードが読みやすくなることがあります。 Especially when the first declaration inside the namespace(s) is preceded by a comment, this becomes a special case of the previous rule, helping the comment to "attach" to the subsequent declaration. 特に、名前空間内にある最初の宣言が、その宣言に関するコメントで始まっている場合、これは一つ前のルールの特殊ケースにあたり、そのコメントが後に続く宣言に係ることを助ける働きがあります。
ルールの例外
The coding conventions described above are mandatory. ここまでに説明したコーディング規約はすべて必須のものです。 However, like all good rules, these sometimes have exceptions, which we discuss here. しかし、すべての良いルールがそうであるように、これらのルールにも例外があります。ここでは、それらの例外的な事項について議論します。
ルールに沿っていない既存のコード
You may diverge from the rules when dealing with code that does not conform to this style guide. このスタイルガイドのルールに準拠しないコードを取り扱う際には、このスタイルガイドのルールから外れてもかまいません。
If you find yourself modifying code that was written to specifications other than those presented by this guide, you may have to diverge from these rules in order to stay consistent with the local conventions in that code. このガイドで示されている仕様とは異なる仕様に沿って書かれたコードを編集する場合には、そのコードが従うコーディング規約との一貫性を保つために、このガイドのルールから外れなければならないかもしれません。 If you are in doubt about how to do this, ask the original author or the person currently responsible for the code. もし、これについてどうすべきか迷ったときは、コードの原著者や現在の責任者と相談してください。 Remember that consistency includes local consistency, too. 「一貫性」には、このような局所的な一貫性を保つことも含まれています。そのことを忘れないでください。
Windowsのコード
Windows programmers have developed their own set of coding conventions, mainly derived from the conventions in Windows headers and other Microsoft code. Windowsプログラマーの間では、独自のコーディング規約が開発されてきました。これらは主にWindowsのヘッダーファイルや、その他のMicrosoftのコードが従っているコーディング規約から派生してきたものです。 We want to make it easy for anyone to understand your code, so we have a single set of guidelines for everyone writing C++ on any platform. 私たちは、コードが誰にとっても簡単に理解できるようになることを望んでいるため、プラットフォームに依存しない、すべてのC++を書く人に向けた、唯一のガイドラインを用意しています。
It is worth reiterating a few of the guidelines that you might forget if you are used to the prevalent Windows style: 一般的なWindowsスタイルに慣れているプログラマーにとって忘れてしまいがちなルールについて、ここでおさらいしておきましょう。
-
Do not use Hungarian notation (for example, naming
an integer
iNum
). ハンガリアン記法 (たとえば、整数型にiNum
と名付ける命名規則) は使いません。 Use the Google naming conventions, including the.cc
extension for source files. ソースコードの拡張子が.cc
であることも含めて、Googleの命名規則に従ってください。 -
Windows defines many of its own synonyms for
primitive types, such as
DWORD
,HANDLE
, etc. Windowsでは、DWORD
やHANDLE
などの、組み込み型に対する独自のシノニム(同義語)が大量に定義されています。 It is perfectly acceptable, and encouraged, that you use these types when calling Windows API functions. Windows APIを呼び出すときにこれらの型を使用することはまったく問題なく、むしろ推奨されることです。 Even so, keep as close as you can to the underlying C++ types. ただし、その場合であっても、できる限り基礎となるC++の型から離れないようにしてください。 For example, useconst TCHAR *
instead ofLPCTSTR
. たとえば、LPCTSTR
ではなく、const TCHAR *
を使用してください。 - When compiling with Microsoft Visual C++, set the compiler to warning level 3 or higher, and treat all warnings as errors. コンパイラにMicrosoft Visual C++を使う場合は、コンパイラの警告レベルを3以上にし、また、すべての警告をエラーとして扱うように設定してください。
-
Do not use
#pragma once
; instead use the standard Google include guards.#pragma once
は使わないでください。かわりに、Google標準のインクルードガードを使用してください。 The path in the include guards should be relative to the top of your project tree. インクルードガードは、プロジェクトツリーのルートからの相対パスを使用して命名します。 -
In fact, do not use any nonstandard extensions,
like
#pragma
and__declspec
, unless you absolutely must.#pragma
や__declspec
などの非標準の拡張については、どうしてもそれを必要とする場合を除いて、使用しないでください。 Using__declspec(dllimport)
and__declspec(dllexport)
is allowed; however, you must use them through macros such asDLLIMPORT
andDLLEXPORT
, so that someone can easily disable the extensions if they share the code.__declspec(dllimport)
と__declspec(dllexport)
は使用してもかまいませんが、その場合には必ずDLLIMPORT
やDLLEXPORT
のようなマクロを通して使用してください。そのようにしておくことで、コードが (他のプラットフォームなどと) 共有されるときに、簡単にそれらの拡張を無効化できるようになります。
However, there are just a few rules that we occasionally need to break on Windows: また、Windowsにおいては、いくつかのルールについて、破らざるをえない場合があります。そのようなルールについて以下に説明します。
- Normally we strongly discourage the use of multiple implementation inheritance; however, it is required when using COM and some ATL/WTL classes. 通常は、実装の多重継承は強く非推奨としていますが、COMやATL/WTLのクラスを扱う場合には、むしろ、多重継承を行うことがほぼ必須となってきます。 You may use multiple implementation inheritance to implement COM or ATL/WTL classes and interfaces. COMやATL/WTLのクラスやインターフェースを実装するためであれば、実装の多重継承を行ってもかまいません。
-
Although you should not use exceptions in your own
code, they are used extensively in the ATL and some
STLs, including the one that comes with Visual C++.
通常は、例外をユーザーコードで扱うべきではありませんが、ATLやSTLやその他のVisual C++由来のコードにおいて、例外は広くわれています。
When using the ATL, you should define
_ATL_NO_EXCEPTIONS
to disable exceptions. ATLを使う場合には、_ATL_NO_EXCEPTIONS
を定義して、例外を無効にしてください。 You should investigate whether you can also disable exceptions in your STL, but if not, it is OK to turn on exceptions in the compiler. また、STLを使う場合は、そのSTLで例外を無効にできないか確認してください。例外を無効にできない場合には、コンパイラの設定を変更し、例外を有効にしてかまいません。 (Note that this is only to get the STL to compile. (ただし、これは、あくまで、STLをコンパイルできるようにするための処置です。 You should still not write exception handling code yourself.) この場合においても、ユーザーコードにおいて、例外を処理するコードを書いてはいけません。) -
The usual way of working with precompiled headers
is to include a header file at the top of each source
file, typically with a name like
StdAfx.h
orprecompile.h
. 一般的なプリコンパイル済みヘッダーの使い方は、各ソースファイルの先頭でそれを#include
する手法です。 典型的には、プリコンパイルヘッダーにはStdAfx.h
やprecompile.h
といったファイル名が付けられます。 To make your code easier to share with other projects, avoid including this file explicitly (except inprecompile.cc
), and use the/FI
compiler option to include the file automatically. 他のプロジェクトとコードを共有しやすくするために、 (precompile.cc
以外では) 明示的にこのファイルを#include
するのを避けてください。かわりに、コンパイラオプションの/FI
を使って、そのファイルが自動的にインクルードされるように設定してください。 -
Resource headers, which are usually named
resource.h
and contain only macros, do not need to conform to these style guidelines. リソースヘッダーファイル (通常はresource.h
という名前を持ち、マクロのみを含んでいるファイル) については、このスタイルガイドのルールに従う必要はありません。