9

重複の可能性:
C++ での const 宣言の違い

#include <iostream>

class Bar{};

void foo(const Bar x){}  //l5
void foo(Bar x){}        //l6
void foo(Bar const x){}  //l7

////pointer functions

void foo(const Bar* x){} //l11
void foo(Bar* x){}       //l12
void foo(Bar* const x){} //l13

コンパイラ出力: (簡単l5に言えばl6l7競合; ただしl12l13競合のみ)

untitled.cpp:6:6: error: redefinition of ‘void foo(Bar)’
untitled.cpp:5:6: error: ‘void foo(Bar)’ previously defined here
untitled.cpp:7:6: error: redefinition of ‘void foo(Bar)’
untitled.cpp:5:6: error: ‘void foo(Bar)’ previously defined here
untitled.cpp:13:6: error: redefinition of ‘void foo(Bar*)’
untitled.cpp:12:6: error: ‘void foo(Bar*)’ previously defined here

何が起こっている?

  1. それぞれの宣言の意味は何ですか
  2. 3 つの宣言すべてがオブジェクト関数と競合するのに、ポインター関数と競合するのは 2 つだけなのはなぜですか?
  3. キーワードが含まれていなくても、l12との間に競合があることを詳しく説明してくださいl13l12const
  4. 些細な質問で本当にすみません
4

6 に答える 6

6

「問題」はconst、パラメーターの値の性質がオーバーロードに関与していないことです!

まず、Bar constconst Barはすでに同じ意味であるため、自動的に問題が発生します。ただし、関数パラメーターとしてconstオーバーロードには適用されないためBar、関数のバージョンも同じように見えます。パラメーターのconstは、関数本体で変更するつもりがないことをコンパイラーに伝えるだけです。

同じ理由で、Bar*Bar* constは同じように扱われます:constはパラメーターの値 (指されているものではない) に適用され、オーバーロードには関与しないため、同じ関数を定義しました。

一方、まったく異なるものを意味します:オブジェクトconst Bar*への非 const ポインター(型)。型が異なるため、オーバーロードに参加し、その関数を一意にすることができます。constBar

于 2013-01-09T18:04:37.490 に答える
2

型名の前後に const を付けるかどうかは問題ではありません。

15 と 17 は、同じパラメーター引数リストを持ちます。
これら 2 つの関数は、同じプロトタイプを持つと見なされ、重複しています。

機能 1

void foo(const int x) {
return;
}

機能 2 - パラメータ引数リストの重複

void foo( int const x) {
return;
}

const の位置は、例の 15 と 17 と同じです。

ウィキペディアによると、どちらも機能します。

http://en.wikipedia.org/wiki/Const-correctness

于 2013-01-09T17:54:21.127 に答える
1

基本的に、C++ は関数を呼び出すときに値をコピーするため、呼び出し元の観点から最初の 3 つを区別するものは何もありません。(呼び出し元は、関数が渡される独自の値を変更できないことを知っているため、すべての関数パラメーターは、とにかく呼び出し元の観点から、多くの点で暗黙的に一定です)。

ただし、ポインターについて話す場合、定数へのポインターと非定数へのポインターを渡す場合、呼び出し元に違いがあります (一方は変更されず、もう一方は変更される可能性があります)。これが、l11 と l12 が競合しない理由です。

ただし、l12 と l13 はどちらも Bar* へのポインターであるため競合します (一方は const ポインターであり、もう一方はそうではないため、l5-l7 と同じ問題であり、呼び出し元に違いはありません)。

この最後のポイントは少しトリッキーかもしれません - whileint const *aは と同じですがconst int *a、これらはと同じではないint * const aことに注意してください。最初の 2 つは定数 int へのポインターであり、もう 1 つは int への定数ポインターです (つまり、ポインターの値後で変更することはできません)。

于 2013-01-09T17:58:19.440 に答える
0

最初の 3 つが競合を引き起こす理由は、どのような場合でもコンパイラがどの関数を使用するかを判断できないためです。コンパイラを呼び出すとfoo(BarObject);、宣言されているかどうかに関係なく、それらのいずれかを使用できBarObjectますconst

ただし、ポインターとしてパラメーターを持つものでは、 if を呼び出すとfoo(BarPointer);、コンパイラーは pick を選択します。これは、指しているオブジェクトが関数内で変更されないことを保証するためです (最初の 3 つのように値で渡す場合とは異なります)。そうでない場合、呼び出す必要があるかどうか、または「パラメーターとして渡されたもの以外を指すことができない」という意味があるため、呼び出し元には関係ありません。BarPointerconst Bar* BarPointer;]11const]12]13Bar* const xx

宣言への小さな参照:

const Bar x // x is an immutable copy of the original parameter.
Bar const x // same as above and gets compiled, but cdecl says it is a syntax error.

const Bar* x // x points to an object that can't be changed.
Bar* const x // x can't point to any other object than the parameter passed.
于 2013-01-09T17:57:26.500 に答える
0
void foo(const Bar x){}
void foo(Bar const x){}

上記の 2 つは、どちらも sayxBar型であり、 it が であるため、同一ですconst

void foo(Bar x){}

これは、関数の実装の詳細xであるかどうかが関数の署名からコンパイラによって破棄されるため、上記の 2 と競合します。したがって、3 つの関数はすべて同じ署名を持つことになります。constvoid foo( Bar x )

void foo(Bar* x){}
void foo(Bar* const x){}

これは前のケースと似ています。xつまり、関数内の何か他のものをconst再ポイントすることはありません。xどちらの場合も、指しているBarオブジェクトは non-です。したがって、の性質は関数の実装の詳細です。xconstconstx

void foo(const Bar* x){}

ここでは、 が であるオブジェクトをx指していることを示しています。これは前の 2 つのケースとは異なるため、競合はありません。Barconst

于 2013-01-09T18:03:04.563 に答える
0

最初の 3 つの関数については、変数がconstによって転送される場合のオーバーロードの解決には関係ありません。スタック上に作成された引数のコピーであり、このコピーが外部 (呼び出し元) の観点から変更されたかどうかは意味がありません。関数自体(内部)にとって重要です。

2 番目のケース、ポインター ベースの関数の場合、コピーはスタック上に作成されず、外部 (呼び出し元) の観点からは、関数が引数の値を変更するかどうかを意味するため、関数のオーバーロード解決の重要な部分です。

最後の 2 つの関数では、say to a compiler を使用します。 へのxポインターがあり、I が指すBarこのBarxは変更される可能性があります。しかし、最初のケースでは、2 番目のケースとは反対に、ポインターx自体の値を変更できます(たとえば、別の を指す)。Barそして、ここで最初の状況です。ポインター自体のコピーがスタック上にあり、関数内で変更されたかどうかにかかわらず、解決をオーバーロードする意味がありません。

于 2013-01-09T18:16:39.930 に答える