値パラメータの定数の正確性
-
19-09-2019 - |
質問
関数の宣言とその定義が値パラメータについて一致する必要がないという const の正しさに関する疑問がほとんどないことは承知しています。これは、値パラメーターの定数は関数内でのみ重要であるためです。これは問題ありません:
// header
int func(int i);
// cpp
int func(const int i) {
return i;
}
これは本当にベストプラクティスなのでしょうか?誰もやっているのを見たことがないからです。私はこれが議論されている他の場所でこの引用を見ました(出典はわかりません)。
「実際、コンパイラにとって、この const を value パラメータの前に含めるかどうかに関係なく、関数シグネチャは同じです。」
「関数宣言では const 値渡しパラメーターを使用しないでください。パラメータを変更しない場合は、同じ関数の定義内でパラメータを const にしてください。」
2 番目の段落では、宣言に const を入れないようにと記載されています。これは、インターフェイス定義の一部として値パラメーターの定数が無意味であるためだと思います。実装詳細です。
この推奨事項に基づいて、ポインター パラメーターのポインター値にも推奨されますか?(参照を再割り当てできないため、参照パラメータでは意味がありません。)
// header
int func1(int* i);
int func2(int* i);
// cpp
int func1(int* i) {
int x = 0;
*i = 3; // compiles without error
i = &x; // compiles without error
return *i;
}
int func2(int* const i) {
int x = 0;
*i = 3; // compiles without error
i = &x; // compile error
return *i;
}
まとめ: 値パラメータを作成すると、いくつかの論理エラーを検出するのに役立ちます。それはベストプラクティスですか?ヘッダー ファイルから const を削除するという極端なことをしますか?ポインター値を const にすることも同様に便利でしょうか?なぜ、あるいはなぜそうではないのでしょうか?
いくつかの参考文献:
C++ const キーワード - 自由に使用しますか? 関数パラメータに「const」を使用する
const 値パラメータが役立つ例を次に示します。
bool are_ints_equal(const int i, const int j) {
if (i = j) { // without the consts this would compile without error
return true;
} else {
return false;
}
// return i = j; // I know it can be shortened
}
解決
それについての私の見解:
それは悪い考えではありませんが、問題は軽微なので、他のことにエネルギーを費やしたほうが良いかもしれません。
あなたの質問では、エラーが発生する可能性がある場合の良い例を示しましたが、場合によっては次のようなことをしてしまうこともあります。
void foo(const int count ...)
{
int temp = count; // can't modify count, so we need a copy of it
++temp;
/* ... */
}
いずれにせよ、長所と短所はわずかです。
他のヒント
関数の値パラメータを const にするのは不必要であるため、行うべきではないという話を何度も読んだことがあります。
ただし、実装が意図しないことを実行していないことを確認するために、時々役立つことがわかります(質問の最後にある例のように)。
したがって、呼び出し元に価値を追加しないかもしれませんが、実装者としての私に少しの価値を追加することがあります。また、呼び出し元から何も奪うことはありません。したがって、使用しても害はないと思います。
たとえば、バッファへのいくつかのポインタ、つまり開始へのポインタと終了へのポインタを受け取る C 関数を実装しているとします。データをバッファーに入れるつもりですが、最後までオーバーランしないようにしたいと考えています。したがって、関数内には、データを追加するときにポインターをインクリメントするコードがあります。バッファの末尾へのポインタを const
パラメータを使用すると、実際にインクリメントすべきポインタではなく、誤って終了境界ポインタをインクリメントするバグをコード化することがなくなります。
したがって、次のようなシグネチャを持つ fillArray 関数:
size_t fillArray( data_t* pStart, data_t* const pEnd);
誤って増加するのを防ぎます pEnd
本当に増やしたいとき pStart
. 。これは大したことではありませんが、C でプログラミングをしたことがある人なら誰でも、このようなバグに遭遇したことがあると思います。
残念ながら、一部のコンパイラ (Sun CC さん、注目しています!) は、const として宣言された引数と宣言されていない引数を誤って区別しており、未定義の関数に関するエラーが発生する可能性があります。
これはあなたの個人的なスタイルによると思います。
クライアントが関数に渡すことができるものに加算または減算されることはありません。本質的には、コンパイル時のアサーションのようなものです。価値が変わらないことがわかるのであれば、ぜひそうしてください。しかし、他の人がそうする大きな理由は私には見当たりません。
私がこれを行わない理由の 1 つは、value パラメーターの定数性が実装の詳細であり、クライアントが知る必要がないためです。後で (意図的に) 関数を変更して実際にその値を変更する場合は、関数のシグネチャを変更する必要があり、クライアントに再コンパイルが強制されます。
これは、パブリックな仮想メソッドを持たないことを推奨する人がいる理由と似ています (関数の仮想性は、クライアントから隠されるべき実装の詳細です) が、私はその特定の派には属しません。
const キーワードが存在する場合。これは、「i」(const 型) の値は変更できないことを意味します。foo 関数内で「i」の値が変更されると、コンパイラはエラーをスローします。」
const オブジェクトを変更できません
ただし、「*i」を変更すると(つまり、*i = 3;)は、「i」の値ではなく、「i」が指すアドレスの値を変更することを意味します
実際、const 関数は、関数によって変更されるべきではない大きなオブジェクトに適しています。
私たちは皆、時々、他人の C++ コードを解きほぐす必要があります。そして、他の人の C++ コードは定義上完全に混乱しています :D。
したがって、それを解読するために私がいつも行う最初のこと(ローカルおよびグローバルデータフロー)は次のとおりです const
コンパイラが警告するまで、すべての変数定義で。これは、値の引数を const 修飾することも意味し、関数の途中で気付かないうちに変更された変数を回避するのに非常に役立ちます...
だから、他の人がどこでも(値パラメータを含む)constを持っていると本当に感謝します:D
私は次のような状況での const の正しさが好きです。
void foo(const Bar &b) //I know b cannot be changed
{
//do something with b
}
これで使えるようになります b
変更を恐れることはありませんが、コピー コンストラクターのコストを支払う必要はありません。