私は最近遭遇しました Herb Sutterのビデオから C++ 11でconst
およびmutable
の意味がどのように変更されたかについてbitwiseconst(およびthread-safe、結果として)従来の論理的にconst。
5年後、プログラマーはconstのこの新しい意味に移行しましたか?
新しいアプリケーションの作成中にこれを使用しようとしましたが、すべてのプライベートメンバーをミューテックスまたはスレッドセーフ(実装によって)で保護されているため、プライベートメンバーをミュータブルとしてマークしているようです(正しくありません(私は経験が浅く、これの基礎がありません)。
すべてのメンバー関数const
とすべてのメンバー変数mutable
をマークするようにスレッドセーフなアプリケーションを設計する際の良い習慣はありますか?
引用した動画は、上級者向けです。 Herb Sutterは、このオーディエンスを最善の方法でコンセンサスに導こうとしますcommunicate他の人々へのこれらのキーワードの意図、この時代のスレッドセーフになることを切望するすべて。
したがって、このビデオに完全にスレッドセーフなコードを書くために知っておく必要があるすべての内容が含まれているとは期待しないでください。
すべてのプライベートメンバーをミュータブルまたはスレッドセーフ(その実装によって)で保護されているため、すべてのプライベートメンバーをミュータブルとしてマークしています。これは適切に感じられません(経験が浅く、これの根拠がありません)。
以下は、何が露骨に間違っているかの架空の例です。
_class Size
{
mutable int x;
mutable int y;
// ...
public:
void setSize(int newX, int newY) const // (first scream)
{
// modify members "x" and "y", "SAFELY!" (second scream)
}
};
_
setSize
という名前の関数がconst
である可能性がないため、関数のシグネチャが誤って叫びます。関数のコードの実装では、メンバー "x"と "y"をmutable
としてマークする必要がありますが、これも間違っていると思われます。
setSize
関数のコードは関係ありません。関数の名前がその関数がオブジェクトの状態を変更することを示唆している場合は、const
を付けないでください。関数からconst
キーワードを削除するとします。これで、メンバー「x」と「y」でmutable
キーワードを使用する必要がないことがわかりました。問題が解決しました。
言い換えると、const
とmutable
の使用がどちらも直感に反する場合で、そのような扱いにくい使用がペアで発生する場合は、使用方法が間違っていることに疑いを持つ必要があります。
ただし、このビデオでは1つの例外的なケースについて言及しています。クラスに_std::mutex
_メンバーが含まれている場合、ほとんどの場合、クラスをmutable
メンバーとしてマークするのが適切です。説明はビデオをご覧ください。
正しく操作した経験があれば、直感に頼って自然に見えるかどうか、自然は正しいということを確認できます。
関数の名前がオブジェクトの状態を変更してはならないことを示している場合は、const
でマークする必要があり、内部のコードはmadeである必要がありますthread-safe、by notスレッドに対して安全でない操作を実行しています。
これは、上記のリンク先のビデオで表現されている考え方です。スレッドセーフになると、コードのユーザー(自分自身、または仲間のチームメイト)は、特定の機能すべきではないが特定のことを行うことを期待します。
ビデオはこれらの例に言及しています:
bool operator == (const T& other) const
はthis
またはother
を変更しないでください。何でも変更するための比較。 (これは「副作用なし」と呼ばれます。)other
の同じインスタンスが2つのスレッドに渡され、各スレッドがそれをコピーコンストラクターに渡すため、class T { public: T(const T& other) {...} };
がother
を変更することはありません。このコードはどうですか?
_class Size
{
const int x;
const int y;
// ...
public:
Size(int init_x, int init_y)
: x(init_x), y(init_y)
{
}
};
_
このコードはさらに優れています。メンバーでのconst
の正しい使用法を示しています。
ただし、特定の方法でSize
クラスを使用することはできません。たとえば、割り当て演算子を使用してSize
の既存のインスタンスを上書きすることはできません。言い換えると、Size
クラスを実装する際のベストプラクティスに従って、Size
を使用する他のソースコードを再設計する必要がある場合があります。これは、C++プロジェクトにとってconst-nessを重要な考慮事項とする「伝染性の問題」です。
スレッドセーフアプリケーションを設計するときに、すべてのメンバー関数をconstにマークし、すべてのメンバー変数を変更可能にするのは良い習慣ですか?
いいえ、それはnot良い習慣です。
Constメンバー関数は、関数が呼び出されたオブジェクトを変更しないことを通知します。オブジェクトは変更されないため、外部ロックなしで複数のスレッドから関数を呼び出しても安全です。
ただし、constメンバー関数が唯一のスレッドセーフ関数であるとは限りません。他の関数もスレッドセーフにすることができます。const
修飾子を使用して変更関数をスレッドセーフとしてマークすると、誤ったシグナルが送信されます。
const
キーワードの意味はまだ「論理的にconst」です。論理的なconstnessがスレッドの安全性などを考慮する必要があるということだけです。
すべての「論理的にconst」とは、クラスのパブリックインターフェイスの観点からは、const
関数のように見えることを意味します。これは良い概念です。クラスのパブリックインターフェイスが、プログラムの他の部分に違いをもたらす唯一のものだからです。私たちのconst
関数はいくつかの情報をキャッシュに保存したり、ビット単位の状態を別の方法で変更したりするかもしれませんが、それは実装の詳細です:外部コードがビット単位ではないことを外部コードが認識できない場合const
、次に、これをconst
とマークする必要があります-これを可能にするために、一部のメンバーがmutable
とマークされている可能性があります。
C++ 11とそのマルチスレッド機能により、この定義はより複雑になります。関数が本当にビット単位のconst
である場合、複数のスレッドから同時に呼び出すことは自動的に安全です。これは、スレッドセーフではないすべての関数が自動的にnot論理的にconst
であることを意味します。これは、外部コードの観点から、ビット単位のconst
関数とは異なる動作をするためです。 。ビット単位のconst
関数はスレッドセーフです。
したがって、コード内でconst
とマークされている関数がスレッドセーフであることを確認する必要があるのは事実ですが、その逆は当てはまりません。スレッドセーフであるが論理的にはない関数を使用することは完全に合法ですconst
その他の理由。
つまり、コードがconst
以外の関数を呼び出す場合、そのコードは関数がnotスレッドセーフであると想定する必要があります。同時に呼び出す必要がある場合、個々の呼び出しは、外側のスコープ内のミューテックスによって保護する必要があります。現時点では関数がスレッドセーフである場合がありますが、これは実装の詳細であり、関数のシグネチャを変更せずに変更できます。
一方、関数isconst
を非スレッドセーフに変更すると、関数のコントラクトが壊れます。