474

あなたはどこまで行きconstますか?必要なときに関数を作成するだけconstですか、それともすべてを独り占めしてどこでも使用しますか? たとえば、単一のブール値パラメーターを取る単純なミューテーターを想像してください。

void SetValue(const bool b) { my_val_ = b; }

それはconst実際に役に立ちますか?個人的にはパラメータも含めて多用したいのですが、この場合は割に合うのでしょうか?

また、関数宣言のパラメーターから省略できることを知って驚きましたconstが、関数定義に含めることができます。次に例を示します。

.h ファイル

void func(int n, long l);

.cpp ファイル

void func(const int n, const long l)

これには理由がありますか?私には少し珍しいようです。

4

31 に答える 31

478

const呼び出し元のオブジェクトを変更しないため、引数が値で渡される場合は無意味です。

違う。

それは、コードと仮定を自己文書化することです。

あなたのコードに多くの人が取り組んでいて、あなたの関数が自明ではない場合はconst、できる限りすべてをマークする必要があります。非常に強力なコードを書くときは、同僚があなたを何とかしようとしているサイコパスだと常に想定する必要があります (特に、将来は自分自身になることが多いため)。

その上、誰かが先に述べたように、コンパイラが物事を少し最適化するのに役立つかもしれません(それはロングショットですが)。

于 2008-09-22T20:51:03.760 に答える
223

その理由はconst、データのコピーを操作しているため、パラメーターが関数内でローカルにのみ適用されるためです。これは、とにかく関数のシグネチャが本当に同じであることを意味します。これをたくさんするのはおそらく悪いスタイルです。

const私は個人的に参照とポインターパラメーター以外は使用しない傾向があります。コピーされたオブジェクトの場合、実際には問題になりませんが、関数内で意図を通知するため、より安全になる可能性があります。まさしく裁断です。ただし、何かをループするときに使用する傾向がありconst_iterator、それを変更するつもりはないので、const参照型の正確さが厳密に維持されている限り、それぞれに推測します。

于 2008-09-22T20:15:12.630 に答える
171

時々(あまりにも頻繁に!)私は他の誰かのC++コードを解きほぐさなければなりません。そして、他の誰かのC ++コードがほぼ定義上完全に混乱していることは誰もが知っています:)したがって、ローカルデータフローを解読するために私が最初に行うことは、コンパイラが吠え始めるまですべての変数定義にconstを入れます。これは、const-qualifying value引数も意味します。これは、これらが呼び出し元によって初期化された単なるローカル変数であるためです。

ああ、変数がデフォルトでconstであり、非const変数には可変が必要だったらいいのにと思います:)

于 2008-09-22T22:00:23.173 に答える
91

次の 2 行は機能的に同等です。

int foo (int a);
int foo (const int a);

a明らかに、2 番目の方法で定義されている場合、本体で変更することはできませんがfoo、外部との違いはありません。

const本当に便利なのは、参照またはポインターパラメーターを使用する場合です。

int foo (const BigStruct &a);
int foo (const BigStruct *a);

これが言っていることは、 foo は大きなパラメーター、おそらくサイズがギガバイトのデータ構造をコピーせずに取ることができるということです。また、呼び出し元に「Foo はそのパラメーターの内容を変更しません*」と言います。また、const 参照を渡すと、コンパイラは特定のパフォーマンスに関する決定を下すことができます。

*: const 性を捨てない限り、それはまた別の投稿です。

于 2008-09-22T20:36:58.863 に答える
84

余分な余分な const は、API の観点からは良くありません。

値によって渡される組み込み型パラメーターのコードに余計な const を追加すると、APIが乱雑になり、呼び出し元または API ユーザーに意味のある約束ができなくなります (実装を妨げるだけです)。

必要のないときに API に「const」が多すぎるのは、「泣いているオオカミ」のようなものです。最終的には「const」がいたるところにあり、ほとんどの場合何の意味もないため、人々は「const」を無視し始めます。

API の余分な const に対する "reductio ad absurdum" 引数は、これらの最初の 2 つの点に適しています。より多くの const パラメーターが適している場合、const を持つことができるすべての引数は、const を持つ必要があります。実際、それが本当に良いのであれば、 const をパラメータのデフォルトにして、パラメータを変更したいときだけ "mutable" のようなキーワードを持たせたいでしょう。

それでは、できる限り const を入れてみてください。

void mungerum(char * buffer, const char * mask, int count);

void mungerum(char * const buffer, const char * const mask, const int count);

上記のコード行を検討してください。宣言が雑然として長くて読みにくいだけでなく、API ユーザーは 4 つの「const」キーワードのうち 3 つを安全に無視できます。ただし、「const」を余分に使用すると、2 行目が危険になる可能性があります。

なんで?

最初のパラメーターを読み間違えると、char * const buffer渡されたデータ バッファー内のメモリは変更されないと思われるかもしれませんが、これは正しくありません。余分な 'const' は、スキャンしたりすぐに読み間違えたりすると、API に関する危険で不正確な仮定につながる可能性があります。


コード実装の観点からも、余分な const は好ましくありません。

#if FLEXIBLE_IMPLEMENTATION
       #define SUPERFLUOUS_CONST
#else
       #define SUPERFLUOUS_CONST             const
#endif

void bytecopy(char * SUPERFLUOUS_CONST dest,
   const char *source, SUPERFLUOUS_CONST int count);

FLEXIBLE_IMPLEMENTATION が true でない場合、API は以下の最初の方法で関数を実装しないことを「約束」しています。

void bytecopy(char * SUPERFLUOUS_CONST dest,
   const char *source, SUPERFLUOUS_CONST int count)
{
       // Will break if !FLEXIBLE_IMPLEMENTATION
       while(count--)
       {
              *dest++=*source++;
       }
}

void bytecopy(char * SUPERFLUOUS_CONST dest,
   const char *source, SUPERFLUOUS_CONST int count)
{
       for(int i=0;i<count;i++)
       {
              dest[i]=source[i];
       }
}

それは非常にばかげた約束です。呼び出し元にまったく利益をもたらさず、実装を制限するだけの約束をする必要があるのはなぜですか?

これらはどちらも同じ機能の完全に有効な実装ですが、あなたが行ったことは、不必要に片手を後ろに縛られていることだけです。

さらに、それは簡単に(そして合法的に回避される)非常に浅い約束です。

inline void bytecopyWrapped(char * dest,
   const char *source, int count)
{
       while(count--)
       {
              *dest++=*source++;
       }
}
void bytecopy(char * SUPERFLUOUS_CONST dest,
   const char *source,SUPERFLUOUS_CONST int count)
{
    bytecopyWrapped(dest, source, count);
}

ほら、そうしないと約束したにもかかわらず、とにかくそのように実装しました–ラッパー関数を使用しただけです. 映画で悪者が人を殺さないと約束し、部下に殺すように命じたようなものです。

これらの余分な const は、映画の悪役からの約束以上の価値はありません。


しかし、嘘をつく能力はさらに悪化します。

偽の const を使用することで、ヘッダー (宣言) とコード (定義) で const を一致させないことができることに気づきました。const に満足している支持者は、const を定義にのみ入れることができるので、これは良いことだと主張します。

// Example of const only in definition, not declaration
struct foo { void test(int *pi); };
void foo::test(int * const pi) { }

ただし、逆は真です...偽の const を宣言にのみ入れ、定義では無視できます。これは、API の余分な const をより恐ろしいものにし、恐ろしい嘘にするだけです - 次の例を見てください:

struct foo
{
    void test(int * const pi);
};

void foo::test(int *pi) // Look, the const in the definition is so superfluous I can ignore it here
{
    pi++;  // I promised in my definition I wouldn't modify this
}

余分な const が実際に行うことは、実装者が変数を変更したり、非 const 参照で変数を渡したいときに、別のローカル コピーまたはラッパー関数を使用するように強制することで、実装者のコードを読みにくくすることだけです。

この例を見てください。どちらがより読みやすいですか? 2 番目の関数の余分な変数の唯一の理由は、一部の API 設計者が余分な const を投入したためであることは明らかですか?

struct llist
{
    llist * next;
};

void walkllist(llist *plist)
{
    llist *pnext;
    while(plist)
    {
        pnext=plist->next;
        walk(plist);
        plist=pnext;    // This line wouldn't compile if plist was const
    }
}

void walkllist(llist * SUPERFLUOUS_CONST plist)
{
    llist * pnotconst=plist;
    llist *pnext;
    while(pnotconst)
    {
        pnext=pnotconst->next;
        walk(pnotconst);
        pnotconst=pnext;
    }
}

ここで何かを学んだことを願っています。余分な const は、API を混乱させる目障りであり、厄介な問題であり、浅はかで意味のない約束であり、不必要な障害であり、時には非常に危険なミスにつながることもあります。

于 2012-06-14T15:26:34.767 に答える
39

const は C++ のデフォルトであるべきでした。このような :

int i = 5 ; // i is a constant

var int i = 5 ; // i is a real variable
于 2008-09-23T11:43:18.390 に答える
24

生計を立てるために C++ をコーディングしたとき、私はできる限りのことをしました。const を使用することは、コンパイラがあなたを助けるのに役立つ素晴らしい方法です。たとえば、メソッドの戻り値を定数化すると、次のようなタイプミスを防ぐことができます。

foo() = 42

あなたが意味したとき:

foo() == 42

foo() が非 const 参照を返すように定義されている場合:

int& foo() { /* ... */ }

コンパイラは、関数呼び出しによって返される無名の一時変数に値を代入させてくれます。const にする:

const int& foo() { /* ... */ }

この可能性を排除します。

于 2008-09-22T22:07:09.443 に答える
15

comp.lang.c++.moderated hereの古い「Guru of the Week」の記事で、このトピックに関する良い議論があります。

対応する GOTW の記事は、ここの Herb Sutter の Web サイトで入手できます。

于 2009-05-04T22:21:53.857 に答える
9

[in] データのみであり、関数によって変更されない参照 (またはポインター) である関数パラメーターに const を使用します。つまり、参照を使用する目的が、データのコピーを回避し、渡されたパラメーターの変更を許可しないことである場合です。

例で boolean b パラメーターに const を設定しても、実装に制約が課されるだけで、クラスのインターフェイスには影響しません (ただし、通常はパラメーターを変更しないことをお勧めします)。

の関数シグネチャ

void foo(int a);

void foo(const int a);

.c と .h を説明する同じです

アサフ

于 2008-09-22T20:15:24.767 に答える
9

値パラメーターを const と言います。

このバグのある関数を考えてみましょう:

bool isZero(int number)
{
  if (number = 0)  // whoops, should be number == 0
    return true;
  else
    return false;
}

number パラメーターが const の場合、コンパイラーは停止し、バグについて警告します。

于 2008-11-14T13:38:17.597 に答える
6

->*or.*演算子を使用する場合は必須です。

それはあなたが次のようなものを書くのを防ぎます

void foo(Bar *p) { if (++p->*member > 0) { ... } }

これは私がほぼ今行ったものであり、おそらくあなたが意図したことにはなりません。

私が言おうとしたのは

void foo(Bar *p) { if (++(p->*member) > 0) { ... } }

constとの間Bar *にa を入れていたらp、コンパイラはそれを教えてくれました。

于 2012-07-24T00:45:03.683 に答える
5

ああ、難しい。一方では、宣言はコントラクトであり、const 引数を値で渡すことは実際には意味がありません。一方、関数の実装を見ると、引数定数を宣言すると、コンパイラが最適化する機会が増えます。

于 2008-09-22T20:18:31.420 に答える
5

const は、呼び出し元のオブジェクトを変更しないため、引数が値で渡される場合は無意味です。

関数の目的が渡された値を変更することでない限り、参照渡しの場合は const を優先する必要があります。

最後に、現在のオブジェクト (this) を変更しない関数は const として宣言できますし、おそらく宣言する必要があります。以下に例を示します。

int SomeClass::GetValue() const {return m_internalValue;}

これは、この呼び出しが適用されるオブジェクトを変更しないという約束です。つまり、次のように呼び出すことができます。

const SomeClass* pSomeClass;
pSomeClass->GetValue();

関数が const でない場合、コンパイラの警告が発生します。

于 2008-09-22T20:40:11.167 に答える
5

値パラメーターを「const」とマークすることは、間違いなく主観的なものです。

ただし、実際には、例のように、値パラメーターを const としてマークすることを好みます。

void func(const int n, const long l) { /* ... */ }

私にとっての価値は、関数のパラメーター値が関数によって決して変更されないことを明確に示していることです。それらは、最初と最後で同じ値を持ちます。私にとって、これは非常に関数型のプログラミング スタイルを維持することの一部です。

短い関数の場合、引数が関数によって変更されないことは通常かなり明らかであるため、「const」をそこに置くことは間違いなく時間/スペースの無駄です。

ただし、より大きな関数の場合、これは実装ドキュメントの形式であり、コンパイラによって強制されます。

「n」と「l」を使用して計算を行うと、一方または両方が変更された場所を見逃したため、別の結果が得られることを恐れることなく、その計算をリファクタリング/移動できます。

これは実装の詳細であるため、ヘッダーで値パラメーター const を宣言する必要はありません。これは、関数パラメーターを実装で使用する名前と同じ名前で宣言する必要がないのと同じです。

于 2008-09-22T22:58:07.060 に答える
3

可能な限り const を使用する傾向があります。(または、ターゲット言語の他の適切なキーワード。) これを行うのは、コンパイラが他の方法では実行できない追加の最適化を実行できるようにするためです。これらの最適化が何であるかはわからないので、ばかげていると思われる場合でも、常に実行しています。

私が知っている限りでは、コンパイラは const 値パラメータを見て、「ねえ、この関数はとにかくそれを変更していないので、参照渡しでいくつかのクロック サイクルを節約できます」と言うかもしれません。関数のシグネチャが変更されるため、そのようなことはないと思いますが、要点はわかります。多分それはいくつかの異なるスタック操作か何かをします...要点はわかりませんが、コンパイラよりも賢くなろうとすると恥をかくことになるだけです。

C++ には、const-correctness の考え方を伴う追加の荷物があるため、さらに重要になります。

于 2008-09-22T20:36:13.377 に答える
2

コンパイラの最適化について:http ://www.gotw.ca/gotw/081.htm

于 2009-05-05T21:53:39.297 に答える
2

あなたが言及した場合、APIの呼び出し元には影響しません。そのため、一般的に行われていません(ヘッダーでは必要ありません)。関数の実装にのみ影響します。

これは特に悪いことではありませんが、API に影響を与えず、タイピングが増えることを考えると、メリットはそれほど大きくありません。そのため、通常は行われません。

于 2008-09-22T20:17:14.527 に答える
2

できる限り const を使用します。パラメータの const は、値を変更してはならないことを意味します。これは、参照渡しの場合に特に役立ちます。関数の const は、関数がクラス メンバーを変更してはならないことを宣言します。

于 2008-09-22T20:22:42.097 に答える
2

値が渡されるパラメーターに const を使用しません。呼び出し元は、パラメーターを変更するかどうかは気にしません。これは実装の詳細です。

本当に重要なのは、メソッドがインスタンスを変更しない場合、メソッドを const としてマークすることです。そうしないと、多くの const_cast<> が発生するか、メソッド const をマークすると、const とマークされているはずの他のメソッドが呼び出されるため、多くのコードを変更する必要があることに気付く可能性があるためです。

また、変更する必要がない場合は、ローカル変数を const とマークする傾向があります。「動く部分」が分かりやすくなることで、コードが理解しやすくなると思います。

于 2008-09-22T20:24:43.013 に答える
1

パラメーターが値で渡される (そして参照ではない) 場合、通常、パラメーターが const として宣言されているかどうかに大きな違いはありません (参照メンバーが含まれていない限り、組み込み型の問題ではありません)。パラメータが参照またはポインタである場合、通常は、ポインタ自体ではなく、参照/指すメモリを保護することをお勧めします (参照自体を const にすることはできないと思いますが、参照先を変更できないため、それほど重要ではありません)。 . できることはすべて const として保護することをお勧めします。パラメータが単なるPOD(組み込み型を含む)であり、途中でさらに変更される可能性がない場合(たとえば、boolパラメータの例では)、間違いを恐れることなく省略できます。

.h/.cpp ファイル宣言の違いについては知りませんでしたが、ある程度の意味はあります。マシン コード レベルでは、何も「const」ではないため、関数 (.h 内) を非 const として宣言すると、コードは const として宣言した場合と同じになります (最適化は別として)。ただし、関数 (.ccp) の実装内で変数の値を変更しないことをコンパイラに知らせるのに役立ちます。変更を許可するインターフェイスから継承している場合に便利ですが、必要な機能を実現するためにパラメーターを変更する必要はありません。

于 2008-09-22T20:27:59.043 に答える
0

パラメータは値渡しなので、const を指定しても指定しなくても呼び出し元の関数の観点から違いはありません。基本的に、値渡しパラメータを const として宣言しても意味がありません。

于 2008-09-23T11:37:48.497 に答える
0

私はそのようなパラメーターに const を付けません - ブール値 (ブール値 & ではなく) が定数であることは誰もが既に知っているので、それを追加すると、人々は「待って、何?」と考えるようになります。または、参照によってパラメーターを渡していることさえあります。

于 2008-09-22T20:17:44.027 に答える
0

const について覚えておくべきことは、最初から const にする方が、後で入れようとするよりもはるかに簡単だということです。

何かを変更したくない場合は const を使用します。これは、関数が何を行い、何を期待するかを説明する追加のヒントです。それらのいくつか、特に c-string を受け入れるもので実行できる多くの C API を見てきました!

ヘッダーよりも cpp ファイルの const キーワードを省略する傾向がありますが、それらをカットアンドペーストする傾向があるため、両方の場所に保持されます。コンパイラがそれを許可する理由がわかりません。コンパイラの問題だと思います。ベスト プラクティスは、const キーワードを両方のファイルに配置することです。

于 2008-09-22T20:18:00.347 に答える
0

あなたの例のすべての const には目的がありません。C++ はデフォルトで値渡しなので、関数はこれらの int と boolean のコピーを取得します。関数がそれらを変更しても、呼び出し元のコピーは影響を受けません。

したがって、余分な const は避けたいと思います。

  • 彼らは冗長です
  • 彼らはテキストを乱雑にします
  • それらは、渡された値が有用または効率的である可能性がある場合に、渡された値を変更することを妨げます。
于 2008-09-22T20:18:54.383 に答える
-1

const パラメーターは、パラメーターが参照によって渡される場合、つまり参照またはポインターのいずれかである場合にのみ役立ちます。コンパイラが const パラメーターを検出すると、パラメーターで使用される変数が関数の本体内で変更されていないことを確認します。値渡しのパラメーターを定数として作成したいのはなぜですか? :-)

于 2008-09-22T20:27:49.400 に答える
-1

とにかく、関数は変数のコピーしか変更できないため、値パラメーターを「const」にする理由は実際にはありません。

"const" を使用する理由は、より大きなもの (多くのメンバーを持つ構造体など) を参照渡しする場合です。この場合、関数がそれを変更できないことが保証されます。むしろ、従来の方法で変更しようとすると、コンパイラが文句を言います。誤って変更されるのを防ぎます。

于 2008-09-22T20:19:35.553 に答える
-1

私は質問が「少し」時代遅れであることを知っていますが、私がそれに出くわしたとき、他の誰かが将来そうするかもしれません.

私たちはまだ C スタイルの考え方にとらわれすぎているように思えます。OOP パラダイムでは、型ではなくオブジェクトをいじります。const オブジェクトは、const 以外のオブジェクトとは概念的に異なる場合があります。具体的には、(ビット単位の const とは対照的に) 論理定数の意味で異なります。したがって、関数 params の const の正確性が (おそらく) POD の場合は過度の注意を払っていたとしても、オブジェクトの場合はそうではありません。関数が const オブジェクトで機能する場合は、そのように指定する必要があります。次のコード スニペットを検討してください。

#include <iostream>

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class SharedBuffer {
private:

  int fakeData;

  int const & Get_(int i) const
  {

    std::cout << "Accessing buffer element" << std::endl;
    return fakeData;

  }

public:

  int & operator[](int i)
  {

    Unique();
    return const_cast<int &>(Get_(i));

  }

  int const & operator[](int i) const
  {

    return Get_(i);

  }

  void Unique()
  {

    std::cout << "Making buffer unique (expensive operation)" << std::endl;

  }

};

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void NonConstF(SharedBuffer x)
{

  x[0] = 1;

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ConstF(const SharedBuffer x)
{

  int q = x[0];

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int main()
{

  SharedBuffer x;

  NonConstF(x);

  std::cout << std::endl;

  ConstF(x);

  return 0;

}

ps .: ここでは (const) 参照の方が適切であり、同じ動作が得られると主張するかもしれません。そうですね。他の場所で見たものとは異なる写真を提供しているだけです...

于 2015-04-08T09:14:41.037 に答える