関数は「プログラムの基本的な構成要素」です。そして「ほとんどのインターフェースで最も重要な部分」。これらのステートメントは、C++ コア ガイドラインの機能にルールを導入します。もちろん、両方のステートメントは完全に正しいです。それでは、関数の定義、関数への引数の受け渡し、関数からの値の返しに関する 30 を超えるルールについて詳しく見ていきましょう。
ルールが多すぎるため、各ルールについて詳しくは説明しません。私はルールから物語を作ろうとするので、あなたと私はそれらを心に留めておくことができます.関数を定義するための規則から始めましょう。概要は次のとおりです。
- F.1:意味のある操作を慎重に名前を付けた関数として「パッケージ化」
- F.2:関数は単一の論理演算を実行する必要があります
- F.3:関数を短くシンプルにする
- F.4:コンパイル時に関数を評価する必要がある場合は、
constexpr
と宣言します - F.5:関数が非常に小さく、時間が重要な場合は、インラインで宣言します
- F.6:関数がスローしない可能性がある場合は、
noexcept
と宣言します。 - F.7:一般的な使用では、
T*
を使用します またはT&
スマート ポインターではなく引数 - F.8:純粋な関数を好む
- F.9:未使用のパラメータには名前を付けないでください
関数定義
F.1:意味のある操作を慎重に名前を付けた関数として「パッケージ化」
F.2:関数は単一の論理演算を実行する必要があります
F.3:関数を短くシンプルに保つ
最初の 3 つのルールは非常に明白で、共通の考え方を共有しています。ルール F2 から始めます。関数を作成すると、単一の論理演算 (F2) を実行する 、関数は短くてシンプル(F3)になる可能性が高くなります。 ルールは、画面に収まる必要がある機能について述べています。これで、正確に 1 つの論理演算を実行するこれらの短くて単純な関数ができました。慎重に名前を付ける必要があります (F1)。 これらの慎重に名前が付けられた関数は、組み合わせてより高度な抽象化を構築できる基本的な構成要素です。これで、適切な名前の関数ができ、プログラムについて非常に簡単に推論できます。
F.4:関数がコンパイル時に評価する必要があるため、constexpr
と宣言します。
constexpr 関数は、コンパイル時または実行時に実行できる関数です。 constexpr 関数を定数式で呼び出し、コンパイル時に結果を要求すると、コンパイル時に取得されます。コンパイル時に評価できない引数を指定して constexpr 関数を呼び出すと、通常の実行時関数として使用できます。
constexpr int min(int x, int y) { return x < y ? x : y; } constexpr auto res= min(3, 4); int first = 3; auto res2 = min(first, 4);
関数 min は、コンパイル時に実行される可能性があります。定数式で min を呼び出し、コンパイル時に結果を要求すると、コンパイル時に結果が得られます:constexpr auto res=min(3, 4)。 first は定数式ではないため、通常の関数として min を使用する必要があります:auto res2 =min(first, 4).
constexpr 関数には他にもたくさんあります。それらの構文は、C++11 ではかなり制限されていましたが、C++14 ではかなり快適になりました。これらは、C++ の一種の純粋関数です。 constexpr に関する私の投稿を参照してください。
F.5:関数が非常に小さく、時間がかかる場合-クリティカル、インラインで宣言
このルールを読んで本当に驚きました。なぜなら、オプティマイザーは、インラインで宣言されていない関数をインライン化し、逆に、インラインとして宣言しても関数をインライン化しないからです。結局、インラインはオプティマイザのヒントにすぎません。
constexpr はインラインを意味します。クラスで定義されたメンバー関数、または関数テンプレートについても、デフォルトで同じことが当てはまります。
最新のコンパイラでインラインを使用する主な理由は、One Definition Rule (ODR) を破ることです。複数の翻訳単位でインライン関数を定義できます。これがインラインに関する私の投稿です。
F.6:関数がスローしない可能性がある場合は、それを宣言します noexcept
関数を noexcept として宣言することにより、代替制御パスの数を減らします。したがって、noexecpt はオプティマイザにとって重要なヒントです。
関数が例外をスローできる場合でも、多くの場合、noexcept は非常に理にかなっています。 noexcept は単にそのような場合を意味します:I don't care.その理由は、例外に対応する方法がないことが原因である可能性があります。したがって、例外を処理する唯一の方法は、terminate() を呼び出すことです。
これは、プログラムがメモリ不足になる可能性があるため、スローされる可能性のある noexcept として宣言された関数の例です。
vector<string> collect(istream& is) noexcept { vector<string> res; for (string s; is >> s;) res.push_back(s); return res; }
F.7:一般的な使用のために、T*
または T&
スマート ポインターではなく引数
スマート ポインターを使用して、関数の使用を制限します。この例は要点を明確にしています。
// accepts any int* void f(int*); // can only accept ints for which you want to transfer ownership void u(unique_ptr<int>); // can only accept ints for which you are willing to share ownership void s(shared_ptr<int>); // accepts any int void h(int&);
関数 u と s には、特別な所有権のセマンティックがあります。 u は所有権を譲渡したい、s は所有権を共有したい。関数 s には、わずかなパフォーマンス ペナルティが含まれます。 std::shared_ptr の参照カウンターを増減する必要があります。このアトミック操作には少し時間がかかります。
F.8:純粋な関数を好む
純粋関数は、同じ引数を指定すると常に同じ値を返す関数です。このプロパティは、参照透過性とも呼ばれます。
純粋関数にはいくつかの興味深い特性があります:
関数を分離して考えることができるため、これらのプロパティは広範囲に及ぶ結果をもたらします:
- コードの正確性を簡単に確認
- コードのリファクタリングとテストがより簡単になります
- 関数の結果を記憶できます
- 純粋な関数を並べ替えたり、他のスレッドで実行したりできます。
純粋関数はしばしば数学関数と呼ばれます。
デフォルトでは、C++ には純粋関数型言語 Haskell などの純粋関数はありませんが、constexpr 関数はほぼ純粋です。したがって、純粋性は C++ の規律に基づいています。
完全を期すためだけに。テンプレート メタプログラミングは、命令型言語 C++ に組み込まれた純粋関数型言語です。興味のある方は、テンプレートのメタプログラミングについてこちらをお読みください。
F.9:未使用のパラメータは名前を付けないでください
未使用のパラメーターに名前を付けないと、プログラムが読みやすくなり、未使用のパラメーターに関する警告が表示されなくなります。
次のステップ
以上が関数定義に関するルールでした。次の投稿では、関数へのパラメーターの受け渡しについて書きます。