1

次のコードをコンパイルして、C ++でデフォルトの引数値と関数のオーバーロードを試していましたが、次の出力に驚きました。

Line 19: error: call of overloaded 'add()' is ambiguous

私がコンパイルしたコードは次のとおりです。

#include <iostream>

using namespace std;

void add(int a=1, int b=1){

cout<<a+b;
}

void add(){

int a =2, b=2;
cout<<a+b;
}


int main(){

add();

return 0;
}

なぜそれがあいまいなのか説明はありますか?事前にThx。

4

3 に答える 3

8

両方の署名が呼び出しに一致するためです。

add();

add(1,1)またはとして解釈できますadd()。あなたが書くときvoid add(int a=1, int b=1)、あなたはコンパイラに言っています - 「聞いてください、もし私がaddパラメータなしで呼び出すなら、私はあなたにそれらをデフォルトにすることを望みます1

最も重要なことは、パラメータを指定せずに呼び出すとどうなると思いますか?add()

  • が出力されると予想される場合2は、パラメータを取らないバージョンを削除してください。

  • 印刷されることが予想される場合4は、最初のバージョンからデフォルトのパラメータを削除してください。

于 2012-06-03T00:05:37.710 に答える
2
void add(int a, int b);
void add();

デフォルト値aを指定しないでください。bデフォルト値では、 の呼び出しで最初の関数と 2 番目の関数のどちらを使用する必要があるかをコンパイラが知る方法はありませんadd()

デフォルト値aを指定する必要がある理由はありますか?b

于 2012-06-03T00:11:57.230 に答える
2

オーバーロードの解決は、C++ 標準 (少なくともC++03およびC++11 ) の § 13.3 で定義されています。次の 3 つの部分があります。

  1. 候補関数を決定し、
  2. 候補関数から実行可能な関数を決定し、
  3. 実行可能な最適な関数を選択します。

候補関数

add(オブジェクトではなく) 関数に名前を付けるため、§ 13.3.1.1.1 は候補関数を決定する方法を定義します。add修飾されていない (.または演算子が含まれていない)ため->、条項 3 が適用されます ( C++11 のドラフト n3337から取得):

修飾されていない関数呼び出しでは、名前は -> または によって修飾されていません。演算子であり、一次式のより一般的な形式を持ちます。名前は、関数呼び出しでの名前検索の通常のルール (3.4) に従って、関数呼び出しのコンテキストで検索されます。そのルックアップによって見つかった関数宣言は、候補関数のセットを構成します。名前検索の規則により、候補関数のセットは、(1) 完全に非メンバー関数で構成されているか、(2) 完全に一部のクラス T のメンバー関数で構成されています。(1) の場合、引数リストは、呼び出しの式リスト。[...]

つまり、候補関数は、関数呼び出しのコンテキストで標準的な名前検索によって見つかった関数です。名前検索は § 3.4 で定義されています。多くの場合、§ 3.4.2 (引数依存の名前検索) で追加の候補関数が見つかりますが、問題の関数呼び出しには引数がないため、§ 3.4.1 のみが重要です。特に、第 6 条:

名前空間 N のメンバーである関数のdeclarator-idに続く関数の定義で使用される名前 (ここで、説明のためだけに、N はグローバル スコープを表すことができます) は、そのブロックで使用される前に宣言されなければなりません。それが使用されているか、それを囲むブロック (6.3) の 1 つで使用されているか、名前空間 N で使用する前に宣言するか、N がネストされた名前空間の場合は、N の囲んでいる名前空間の 1 つで使用する前に宣言する必要があります。

つまり、現在の名前空間と親の名前空間が検索され、既に宣言されている関数のみが考慮されます。サンプル コードではmain、名前が のグローバル名前空間で前に宣言されたすべての関数addが候補です:add(int, int)およびadd(). add(float, float)の後に(たとえば) 関数を宣言する場合main、それは候補関数にはなりません。

実行可能な機能

§ 13.3.2:

2 まず、実行可能な関数であるためには、候補関数は、リスト内の引数と数が一致するのに十分なパラメーターを持っている必要があります。
  • リストに m 個の引数がある場合、正確に m 個のパラメーターを持つすべての候補関数が実行可能です。
  • パラメータが m 未満の候補関数は、パラメータ リストに省略記号がある場合にのみ実行可能です (8.3.5)。オーバーロード解決の目的で、対応するパラメーターがない引数は、「省略記号に一致する」と見なされます (13.3.3.1.3)。
  • m 個を超えるパラメーターを持つ候補関数は、(m+1)-st パラメーターにデフォルト引数 (8.3.6) がある場合にのみ実行可能です。オーバーロード解決の目的で、パラメーター リストの右側が切り捨てられ、正確に m 個のパラメーターが存在するようになります。
3 次に、F が実行可能な関数であるためには、各引数に対して、その引数を対応する F のパラメーターに変換する暗黙の変換シーケンス(13.3.3.1) が存在する必要があります。パラメーターに参照型がある場合、暗黙の変換シーケンスには、参照をバインドする操作、および非 const への左辺値参照を右辺値にバインドできず、右辺値参照を左辺値にバインドできないという事実は、関数の実行可能性に影響を与える可能性があります (13.3.3.1.4 を参照)。

引数リストに引数がありません。add()引数が 0 であるため、実行可能です。add(int, int)には 2 つの引数がありますが、最初の引数にはデフォルトの引数があるため、実行可能です。int foo(int&)呼び出しには引数がないため、節 3 の変換は機能しませんが、特に宣言された関数を関数呼び出しにバインドできないことを指摘しているため、節に注意することが重要です。非 const 参照(例: ) は右辺値 (例: リテラル 0) にバインドできないためですfoo(0)。ただし、にバインドできます。int&int foo(const int&)foo(0)

最高の機能

§ 13.3.3 では、関数の部分的な順序付けに関して、名前解決に関して、ある関数が別の関数よりも「優れている」と見なされる方法を定義しています。

  1. 一部の変換は他の変換よりも「優れている」(§13.3.3.2) ため、より優れた引数変換を使用する関数はより優れた関数です。
  2. 1 がより良い関数を決定しない場合、非テンプレート関数はテンプレート関数よりも優れています。
  3. 2 がより良い関数を決定しない場合は、別のものよりも特化された関数テンプレートの特殊化が優れています (「より特化された」とは、§ 14.5.6.2 で定義された部分的な順序付けです)。

引数がないため、条件 1 は使用できません。どちらadd()add(int,int)テンプレートではないため、2 も 3 も使用できません。要するに、どちらの機能も他より優れているわけではありません。

最後に、§ 13.3.3 2 が最終結果を決定します。

他のすべての実行可能な関数よりも優れた関数である実行可能な関数が 1 つだけある場合、それはオーバーロードの解決によって選択された関数です。そうでない場合、呼び出しの形式が正しくありません。

サンプル コードには 2 つの実行可能な関数があるため、呼び出しの形式が正しくありません。

于 2012-06-03T01:55:32.727 に答える