簡単に言うと、割り当てには左側と右側の2つの側面があるためです。
非constバージョン:T& operator() (unsigned i, unsigned j);
主に割り当ての左側を対象としています(つまり、割り当てのターゲットとして使用されます)。
constバージョン:T const& operator() (unsigned i, unsigned j) const;
割り当ての右側のみを対象としています。
ただし、そこでの表現の違いに注意してください。constバージョンは割り当ての右側でのみ使用できますが、non-constバージョンはどちらの側でも使用できます。ただし、const
-qualifiedオブジェクトがある場合は、const-qualifiedメンバー関数しか呼び出せないため、この場合はまったく使用できません。これはまさにあなたが(少なくとも通常は)望んでいることです-それはあなたが変更すべきではないと言ったオブジェクトを変更することを防ぎます(それをconst-qualifiedすることによって)。
これまでのところ、これは通常、論理状態とビット単位のmutate
状態の間に何らかの違いがあるオブジェクトにのみ使用されます。一般的な例は、いくつかの(通常は高価な)計算を怠惰に行うクラスです。値の再計算を回避するために、計算後に値を保存します。
class numbers {
std::vector<double> values;
mutable double expensive_value;
bool valid;
public:
numbers() : valid(false) {}
double expensive_computation() const {
if (valid) return expensive_value;
// compute expensive_value here, and set `valid` to true
}
};
したがって、ここでは、からの結果expensive_computation
は純粋にの値に依存しますvalues
。速度を気にしない場合は、ユーザーがを呼び出すたびに値を再計算するだけで済みますexpensive_computation
。ただし、constオブジェクトで繰り返し呼び出すと、常に同じ結果が生成されます。したがって、一度呼び出すと、再度呼び出される可能性があると想定し、同じ高価な計算を繰り返し実行しないように、値をに保存しますexpensive_value
。次に、ユーザーが再度要求した場合は、値を返すだけです。
つまり、論理的なconst
観点からは、を変更してもオブジェクトは残りますexpensive_value
。オブジェクトの表示状態は変更されません。私たちが行ったのは、constをより迅速に実行できるようにすることだけです。
これが正しくvalid
機能するためには、ユーザーがの内容を変更するたびにfalseに戻す必要がありvalues
ます。例えば:
void numbers::add_value(double new_val) {
values.push_back(new_val);
valid = false;
}
場合によっては、中程度の有効性が必要になることもあります。たとえば、ブール値を使用して現在かどうかを判断するのではなく、expensive_value
追加された数値を正確に把握することで、より迅速に再計算できる場合があります。values
有効かどうか。
おそらく、C++11がとの両方に関していくつかの新しい要件を追加することを追加する必要がconst
ありmutable
ます。長い話を短くするために、ほとんどの状況下で、スレッドセーフであるか、スレッドセーフであるかを確認する必要がありconst
ますmutable
。これに関するハーブサッターのビデオを見たいと思うかもしれません。しかし、彼の結論mutable
はおそらく少し誇張されていると思います(しかし、私の言葉を信じるよりも、あなたが自分で見て決定したほうがいいと思います)。