「get() const」と「getAsConst() 定数」
-
01-07-2019 - |
質問
ある人が、チーム内の C++ スタイルの違いについて教えてくれました。この件については私自身の見解がありますが、興味があるのは、 長所 そして 短所 みんなから来ています。
したがって、2 つのゲッター (1 つは読み取り/書き込み、もう 1 つは読み取り専用) を介して公開したいクラス プロパティがある場合、決まった方法はありません)。これを行うには少なくとも 2 つの方法があります。
class T ;
class MethodA
{
public :
const T & get() const ;
T & get() ;
// etc.
} ;
class MethodB
{
public :
const T & getAsConst() const ;
T & get() ;
// etc.
} ;
それぞれの方法の長所と短所は何でしょうか?
私は C++ の技術的/意味論的な理由により興味がありますが、スタイル上の理由も歓迎します。
ご了承ください MethodB
大きな技術的欠点が 1 つあります (ヒント:汎用コード内)。
解決
まず、getAsConst しなければならない const オブジェクトを受け取りたいときではなく、「this」ポインタが const のときに呼び出されます。したがって、他の問題と同様に、この問題の名前は微妙に間違っています。(「this」が非定数の場合でも呼び出すことはできますが、それはここにもあそこにもありません。)
それを無視すると、getAsConst では何も得られず、インターフェイスを使用する開発者に過度の負担がかかります。ただ「get」を呼び出して必要なものを取得していることを確認するのではなく、現在 const 変数を使用しているかどうか、取得している新しいオブジェクトが const である必要があるかどうかを確認する必要があります。その後、何らかのリファクタリングにより両方のオブジェクトが非定数になった場合、呼び出しを切り替える必要があります。
他のヒント
C++ は、ほぼすべての状況でメソッド A に完全に対処できる必要があります。いつも使っていますが、特に困ったことはありません。
私の意見では、方法 B は OnceAndOnlyOnce に違反するケースです。そして、初めてコンパイルされるコードを書くために const 参照を扱っているかどうかを判断する必要があります。
これはスタイルの問題だと思います。技術的にはどちらも機能しますが、MethodA ではコンパイラの動作が少し難しくなります。私にとって、それは良いことです。
個人的には、インターフェイスがより一貫したものになるため、最初の方法を好みます。また、私にとって getAsConst() は getAsInt() と同じくらい愚かに思えます。
話は変わりますが、クラスのデータ メンバーに非 const 参照または非 const ポインターを返す前に、よく考えてください。これは、理想的には隠されるべきクラスの内部構造を悪用する人々への招待状です。言い換えれば、カプセル化が壊れます。get() const と set() を使用し、他に方法がない場合、または配列の要素への読み取り/書き込みアクセスを許可するなど、本当に意味がある場合にのみ非 const 参照を返します。またはマトリックス。
標準ライブラリによって設定されたスタイルの先例 (つまり、ほんの一例を挙げると begin() と begin() const) を考慮すると、メソッド A が正しい選択であることは明らかです。方法Bを選ぶ人の正気を疑います。
したがって、一般的には最初のスタイルが推奨されます。
ただし、const と非 const の使用を大きく区別したいため、現在取り組んでいるコードベースでは 2 番目のスタイルのバリエーションをかなり使用しています。
私の具体的な例では、getTessellation と getMutableTessellation があります。これは、コピーオンライト ポインターを使用して実装されています。パフォーマンス上の理由から、可能な限り const バージョンを使用するようにしたいので、名前を短くし、作成するつもりもないのに誤ってコピーが発生しないように別の名前にします。
あなたの質問は 1 つの方法のみを扱っているようですが、スタイルについて意見をいただければ幸いです。個人的には、スタイル上の理由から、前者の方が好きです。ほとんどの IDE では、関数の型シグネチャがポップアップ表示されます。
私は最初のほうが好きです。本質的に同じことを行う 2 つのものが同じように見えると、コードの見た目が良くなります。また、非 const オブジェクトを持ちながら const メソッドを呼び出したいということはほとんどないので、それほど心配する必要はありません (最悪の場合、必要なのは const_cast<> だけです)。
1 つ目は、変数の型 (変数の型に関係なく) の変更を許可します。 const
またはそうでない)、コードをさらに変更する必要はありません。もちろん、これは、意図したパスから変更された可能性があることを開発者に通知しないことを意味します。つまり、迅速にリファクタリングできることを重視するか、それとも追加のセーフティ ネットを確保するかを重視するかどうかが重要です。
2つ目は個人的に気になるハンガリー語表記に関するものです。 やめてください 同様に、私はそれに固執します 初め 方法。
私はハンガリー語表記が好きではありません。なぜなら、それはプログラミングにおいて私が通常嫌う冗長性を追加するからです。それは単なる私の意見です。
クラス名を非表示にするため、スタイルに関するこの考え方の材料は、適用される場合と適用されない場合があります。
これら 2 つのオブジェクト、MethodA と MethodB に「get」または「getAsConst」を指示するのは意味がありますか?「get」または「getAsConst」をどちらかのオブジェクトにメッセージとして送信しますか?
メッセージの送信者/メソッドの呼び出し者としての私の見方では、 あなた 値を取得するのは側です。したがって、この「get」メッセージに応答して、何らかのメッセージを MethodA / MethodB に送信し、その結果が取得する必要のある値になります。
例:たとえば、MethodA の呼び出し元が SOA のサービスで、MethodA がリポジトリの場合、サービスの get_A() 内で MethodA.find_A_by_criteria(...) を呼び出します。
私が見た MethodB の主な技術的欠点は、それに汎用コードを適用する場合、const バージョンと非 const バージョンの両方を処理するためにコードを 2 倍にする必要があることです。例えば:
T が順序付け可能なオブジェクトであるとします (つまり、演算子 < を使用して型 T のオブジェクトと比較できます)。また、2 つの MethodA (それぞれ 1) の間の最大値を見つけたいとします。2 つの方法 B)。
MethodA の場合、コーディングする必要があるのは次のとおりです。
template <typename T>
T & getMax(T & p_oLeft, T & p_oRight)
{
if(p_oLeft.get() > p_oRight.get())
{
return p_oLeft ;
}
else
{
return p_oRight ;
}
}
このコードは、型 T の const オブジェクトと非 const オブジェクトの両方で動作します。
// Ok
const MethodA oA_C0(), oA_C1() ;
const MethodA & oA_CResult = getMax(oA_C0, oA_C1) ;
// Ok again
MethodA oA_0(), oA_1() ;
MethodA & oA_Result = getMax(oA_0, oA_1) ;
問題は、この簡単なコードを MethodB 規則に従って何かに適用したいときに発生します。
// NOT Ok
const MethodB oB_C0(), oB_C1() ;
const MethodB & oB_CResult = getMax(oB_C0, oB_C1) ; // Won't compile
// Ok
MethodA oB_0(), oB_1() ;
MethodA & oB_Result = getMax(oB_0, oB_1) ;
MethodB が const バージョンと非 const バージョンの両方で動作するには、すでに定義されている getMax を両方とも使用する必要がありますが、それに次のバージョンの getMax を追加する必要があります。
template <typename T>
const T & getMax(const T & p_oLeft, const T & p_oRight)
{
if(p_oLeft.getAsConst() > p_oRight.getAsConst())
{
return p_oLeft ;
}
else
{
return p_oRight ;
}
}
結論として、const の使用に関してコンパイラを信頼しないことにより、1 つで十分なはずの汎用関数を 2 つ作成するという負担が発生します。
もちろん、十分な妄想を踏まえた上で、2 番目のテンプレート関数は getMaxAsConst と呼ばれるべきでした...したがって、問題はすべてのコードに伝播することになります...
:-p