web-dev-qa-db-ja.com

引数識別子のない関数がC ++で有効なのはなぜですか?

型のみで識別子を持たない引数を持つC++の関数を考えると、

 void foo1(int, int, int){cout << "called foo1";}

私はそれを次のように呼ぶことができます:

int main()
{
    foo1(10, 10, 10);
}

なぜこれはC++で有効な構成体ですか?これはC++の単なる特異性ですか、それともこの種の宣言には実際に何らかの目的がありますか?実際に何らかの方法で渡された引数にアクセスできますか? (この種のメソッド宣言はJavaでは機能しません。)

38
programmerravi

次のプロトタイプを満たす機能を提供する必要がある場合を考えます

void dostuff(int x, int y, int z);

また、2D空間で操作しており、実装内でzを使用しないでください。あなたはできる

void dostuff(int x, int y, int z)
{
    // use x and y
}

zを無視しますが、コンパイラはおそらくzを定義したが使用していないことに気付き、間違いを犯している可能性があることを警告します。代わりにできます

void dostuff(int x, int y, int )
{
    // use x and y
}

zの定義は省略します。コンパイラーは、3番目のパラメーターが必要ないことを認識しているため、3番目のパラメーターを受け入れて静かに破棄します。

このようなエラーのため、単に警告をオフにしたくない

void dostuff(int x, int y, int z)
{
    for (int z = 0; z < MAX; z++)
    {
        // use x and y and z, the local z. 
    }
}

名前の不適切なループインデックスがパラメーターzをシャドウします。呼び出し元の入力は無視されるようになり、悪い結果を招く可能性があります。このエラーは、特にローカルzが複雑な関数のどこかに深く埋まっている場合、マーク1の目玉で見つけるのが難しいことがよくあります。

コンパイラーがコードの潜在的なバグを発見できるときはいつでも、活用してください。それはあなたにとってより少ない仕事を意味します。

40
user4581301

のような宣言

void foo1(int, int, int){cout << "called foo1";}

関数で要件を満たしたいことを宣言で明確に示しています-例:基本クラスまたはインターフェースの特定の関数をオーバーライドします。そこに宣言することができます

virtual void foo1(int something, int another, int andAnother) = 0;

ただし、引き渡されたパラメータを使用するつもりはありません。

別の例は、関数を次のように引き渡したい場合です。 3つのintパラメーターを持つvoid関数への関数ポインターを予期する別の関数。

void giveMeFoo( void (*fn)(int, int, int) ) { ... }

さらに、パラメーターが宣言されている場合、より高い警告レベルでは、関数本体で評価されない警告が発行されます。パラメーター名を残しておくことで、これを回避できます。

名前のないパラメーターは、関数本体では実際にはアクセスできなくなります-意図的に。 user4581301は、その理由をうまく説明しています。

あなたの例のようにパラメータ名なしでスタンドアロン関数を宣言することは、上記の使用法のために許可されていますが、ほとんどの場合、明らかに意味をなしません。理にかなっている例は、コメントセクションにあります。ライブラリーを作成しており、後方互換性を維持したい場合は、パラメーター名のないスタンドアロン関数の別の例があります(ライブラリー関数はパラメーターをもう必要としませんが、パブリックヘッダー宣言を壊したくない)または、将来使用するためにパラメータを予約します。

15
user2328447

はい。 C++では合法です。

C++ 11 n3337標準8.4.1(p6)関数定義:

注:未使用のパラメーターに名前を付ける必要はありません。例えば、

void print(int a, int) {
    std::printf("a = %d\n", a);
}

C++ 14標準:

[8.3.5.11] 識別子はオプションでパラメータ名として提供できます。関数定義に存在する場合、パラメータに名前を付けます(「仮引数」と呼ばれることもあります)。 [注:特に、パラメータ関数定義では名前もオプションであり、異なる宣言のパラメーターに使用される名前であり、関数の定義は同じである必要はありません。

10
msc

それは合法であり、なぜあなたが疑問に思っているなら:

通常、名前のない引数は、コードの単純化または拡張の事前計画から生じます。どちらの場合も、引数を使用せずにそのままにしておくと、呼び出し元が変更の影響を受けないことが保証されます。

抜粋:Bjarne Stroustrup。 「C++プログラミング言語、第4版」。

7
Carlos

アイデアは、関数が呼び出されるすべてのコードを変更せずに、後でプレースホルダーを使用するように関数定義を変更したい場合があるということです

関数宣言の引数は、識別子なしで宣言できます。これらをデフォルトの引数で使用すると、少しおかしく見えることがあります。で終わることができます:

_void f(int x, int = 0, float = 1.1);
_

C++では、関数定義に識別子も必要ありません。

_void f(int x, int, float flt) { /* ... */ }
_

関数本体では、xとfltを参照できますが、名前がないため、中間の引数は参照できません。ただし、f(1)またはf(1,2,3.0)の場合でも、関数呼び出しはプレースホルダーの値を提供する必要があります。この構文により、引数を使用せずにプレースホルダーとして配置できます。

0
BattleTested