79

項目03ではEffective C++、可能な限りconstを使用してください。

class Bigint
{
  int _data[MAXLEN];
  //...
public:
  int& operator[](const int index) { return _data[index]; }
  const int operator[](const int index) const { return _data[index]; }
  //...
};

const int operator[]との違いはありませんint& operator[]

しかし、どうですか:

int foo() { }

const int foo() { }

それらは同じようです。

私の質問は、なぜ私たちがconst int operator[](const int index) const代わりに使用するのint operator[](const int index) constかということです。

4

12 に答える 12

31

非参照/非ポインターの右辺値に修飾を追加する価値はほとんどなく、組み込みに追加しても意味がありませんconst

ユーザー定義型の場合、const修飾により、呼び出し元が返されたオブジェクトでconstメンバー関数を呼び出すことができなくなります。たとえば、与えられた

const std::string foo();
      std::string bar();

それから

foo().resize(42);

禁止されますが、

bar().resize(4711);

許可されます。

のような組み込みの場合、このようintな右辺値はとにかく変更できないため、これはまったく意味がありません。

(ただし、 Effective C++operator=()で参照の戻り値の型を作成することについて説明したことは覚えていますconstが、これは考慮すべきことです。)


編集:

スコットは確かにそのアドバイスをしたようです。もしそうなら、上記の理由により、C++98 と C++03 でさえ疑わしいと思います。C++11 の場合、Scott 自身が発見したように、明らかに間違っていると思います。効果的な C++、第 3 版正誤表。、彼は書いています(または不平を言った他の人を引用します):

テキストは、すべての値による戻り値が const であるべきであることを暗示していますが、非定数値による戻り値が適切な設計である場合を見つけるのは難しくありません。たとえば、呼び出し元が空のベクターで swap を使用する std::vector の戻り値の型戻り値の内容をコピーせずに「取得」します。

以降:

値渡し関数の戻り値 const を宣言すると、C++0x で右辺値参照にバインドされなくなります。右辺値参照は C++ コードの効率を向上させるように設計されているため、関数シグネチャを指定するときは、const の戻り値の相互作用と右辺値参照の初期化を考慮することが重要です。

于 2012-08-21T09:03:37.207 に答える
18

Meyers のアドバイスの要点を見落としているかもしれません。本質的な違いはconst、メソッドの修飾子にあります。

これは、クラスの状態を変更できることを意味する非 const メソッドです (const末尾の no に注意してください)。

int& operator[](const int index)

これはconstメソッドです(const最後に注意)

const int operator[](const int index) const

パラメータと戻り値の型についてはどうですか。 と の間にはわずかな違いがintありconst intますが、アドバイスのポイントには関係ありません。非 const オーバーロードが返すことに注意する必要がありますint&。これは、たとえば、に割り当てることができることを意味num[i]=0し、const オーバーロードは変更不可能な値を返します (戻り値の型がintまたはであるかどうかに関係なくconst int)。

私の個人的な意見では、オブジェクトが値で渡される場合const修飾子は不要です。この構文はより短く、同じことを達成します

int& operator[](int index);
int operator[](int index) const;
于 2012-08-21T08:40:10.190 に答える
15

値を const として返す主な理由は、 のようなことを言うことができないようにするためですfoo() = 5;。プリミティブ型の右辺値に割り当てることができないため、これは実際にはプリミティブ型の問題ではありませんが、ユーザー定義型 ( (a + b) = c;、オーバーロードされた などoperator+) の問題です。

私はいつも、その正当化はかなり薄っぺらだと思ってきました。ぎこちないコードを書こうとする人を止めることはできません。私の意見では、この特定のタイプの強制は実際には何のメリットもありません。

C++11 では、このイディオムが実際に行っているかなりのがあります: const として値を返すと、移動の最適化が妨げられるため、可能な限り避ける必要があります。基本的に、私はこれをアンチパターンと考えています。

これは、C++11 に関する接線関連の記事です。

于 2012-08-21T08:44:00.077 に答える
9

その本が書かれたとき、アドバイスはほとんど役に立ちませんでしたが、たとえば、ユーザーfoo() = 42;が何かを永続的に変更することを期待して書くのを防ぐのに役立ちました。

の場合、非参照を返すoperator[]非オーバーロードも提供しないと、少し混乱する可能性がありますが、値の代わりに参照またはプロキシ オブジェクトを返すことで混乱を防ぐことができます。constconstconst

const最近では、結果を (非) 右辺値参照にバインドできなくなるため、これは悪いアドバイスです。

(コメントで指摘されているように、 のようなプリミティブ型を返す場合int、言語ではそのような型の に割り当てることができないため、問題は意味がありませんrvalue。ユーザー定義型を返すことを含む、より一般的なケースについて話しています。 )

于 2012-08-21T08:40:51.860 に答える
2

プリミティブ型 ( などint) の場合、結果の const-ness は重要ではありません。クラスの場合、動作が変わる可能性があります。たとえば、関数の結果に対して非 const メソッドを呼び出すことができない場合があります。

class Bigint {
    const C foo() const { ... }
     ...
}

Bigint b;
b.foo().bar();

bar()が の const メンバ関数でない場合、上記は禁止されていCます。一般的に、意味のある方を選択してください。

于 2012-08-21T08:42:03.997 に答える
0

2 つのバージョンの専門性を中心に展開するいくつかの適切な回答があります。プリミティブ値の場合、違いはありません。

ただし、私は常にconstコンパイラーではなくプログラマーのためにそこにいると考えてきました。を書くときconst、あなたは明示的に「これは変えてはならない」と言っています。それに直面しましょう、const通常はとにかく回避できますよね?

を返すときはconst、その関数を使用するプログラマーに、値を変更してはならないことを伝えています。そして、彼がそれを変更している場合、彼/彼女はそうする必要がないので、おそらく何か間違ったことをしている.

const編集:「可能な限り使用する」ことも悪いアドバイスだと思います。意味のある場所で使用する必要があります。

于 2012-08-21T09:01:25.220 に答える
0

それを見てください:

const int operator[](const int index) const

constステートメントの終わり。このメソッドは定数に対して呼び出すことができると説明しています。

一方、あなただけを書くとき

int& operator[](const int index)

非 const インスタンスでのみ呼び出すことができ、以下も提供します。

big_int[0] = 10;

構文。

于 2012-08-21T08:42:02.637 に答える
0

オーバーロードの 1 つは、配列内の項目への参照を返しています。これを変更することができます。

int& operator[](const int index) { return _data[index]; }

もう 1 つのオーバーロードは、使用する値を返します。

const int operator[](const int index) const { return _data[index]; }

これらの各オーバーロードを同じ方法で呼び出すため、使用時に値が変更されることはありません。

int foo = myBigInt[1]; // Doesn't change values inside the object.
myBigInt[1] = 2; // Assigns a new value at index `
于 2012-08-21T08:42:07.260 に答える
0

配列インデックス演算子 ( operator[]) の例では、違いがあります。

int& operator[](const int index) { /* ... */ }

インデックスを使用して、配列内のエントリを直接変更できます。たとえば、次のように使用します。

mybigint[3] = 5;

2 番目のconst int operator[](const int)演算子は、値のみをフェッチするために使用されます。

ただし、関数からの戻り値として、たとえば単純な型のint場合は問題ありません。それが問題になるのは、より複雑な型を返す場合、たとえば astd::vectorであり、関数の呼び出し元にベクトルを変更させたくない場合です。

于 2012-08-21T08:42:20.330 に答える