次のコードは非常に便利で害はないと思います。
auto fn = [](bool b = false) -> int // NOT legal in C++11
{
return b ? 1 : 0;
};
C++ 11がラムダ式のデフォルト引数を明示的に禁止しているのはなぜですか?
背後にある理論的根拠と考慮事項について疑問に思います。
C++ 11標準が言う「何」ではなく「なぜ」を知りたいのです。
ラムダがデフォルトの引数を持つことができないという本当の理由はありません。ただし、ラムダを使用する主な方法は2つあり、型システムを変更せずにデフォルトの引数を許可するのはそのうちの1つだけです。
ラムダを直接呼び出すことも、テンプレートを介して呼び出すこともできます。この場合、デフォルトのパラメータで問題なく動作します。
_std::function
_を介してラムダを呼び出すことができます。デフォルトのパラメータは、型システムを変更しないと機能しません。
私の推測では、C++ 11で作成する新しい関数は、通常_std::function
_パラメーターを取ります。この方法では、関数は取りません。 haveをテンプレートにするか、渡されるラムダとファンクターごとにインスタンス化する必要はありません。
std::function
_(または関数ポインタ)がデフォルトをとれないのはなぜですか?そのような関数のタイプが何であるかは明らかではありません。
std::function<int(bool)>
の場合、デフォルトでどのように呼び出しますか? (できません。)
std::function<int(bool=false)>
の場合、どのタイプと互換性があり、変換はどのように機能しますか? std::function<int()>
に変換できますか? std::function<int(bool=true)>
はどうですか?
std::function<int(bool=default)>
、thenのような新しいものの場合、どのタイプと互換性があり、変換はどのように機能しますか?
基本的に、これは、標準を切り替えて関数ポインタ/ _std::function
_にデフォルトの引数を処理させることができる単なるスイッチではありません。通常の関数のデフォルト引数は、関数の宣言からの情報を使用して処理されます。この情報は、ラムダまたは関数ポインターの呼び出しサイトでは使用できません。したがって、デフォルトに関する情報を関数型にエンコードしてから、変換と互換性に関するすべての非自明なルールを理解する必要があります。
したがって、なぜそのような機能が追加されるのかについて説得力のある事例を考え出し、委員会を説得する必要があります。
私はこの質問に答えていません。しかし、追加するのはあまり便利な機能ではないと思います。できればこの回答を削除しますが、受け入れられました。できれば反対票を投じますが、それは私のものです。 C'est lavie。
質問へのコメントが示唆しているように、デフォルトの引数がない理由はおそらくありませんtechnical理由はありません。しかし、逆の質問は「デフォルトの引数の実用的理由はありますか?」です。これに対する答えは「いいえ」だと思いますが、その理由はここにあります。
ラムダを呼び出すためにできることの1つは、すぐに呼び出すことです
_[] { printf("foo"); }();
_
用途が限られていると思いますので、次に進みましょう。ラムダを呼び出す他の唯一の方法は、最初にラムダを変数にバインドすることです。ここにはいくつかのオプションがあります。
auto foo = [] { printf("foo"); }; foo();
を取得しますvoid (*foo)() = [] { printf("foo"); }; foo()
。もちろん、これはラムダがキャプチャしない場合にのみ機能します。それでは、それぞれの場合のデフォルト引数の有用性を見ていきましょう。
これらの点は、ラムダへのデフォルトの引数が実際にはそれほど有用ではないと言うのに十分だと思います。また、デフォルトの引数がある場合、ラムダの型に関する問題について話している人もいますが、これは問題のないIMOです。同じことを実行し、doesがデフォルトの引数を持つ独自のファンクターをいつでも作成できます。また、関数ポインタへの劣化についても、言うことはあまりありません。通常の機能を取る
_void func(int i = 0)
{
}
_
そしてそれのアドレスを取ります。あなたは何を得ますか? void (*)(int)
。ラムダが異なるルールに従う理由はありません。
ラムダのデフォルト引数が機能することを許可するための実際の「技術的」制限がないことに同意しますそれ自体場合によっては。関数の型はデフォルトの引数の影響を受けないため、ポインターとauto
が壊れることはありません。しかし、それがこれがひどく実用的ではない理由でもあります。
どうして?
デフォルトの引数は、関数シグネチャの一部ですが、関数型の一部ではないため、次のようになります。
[C++11: 1.3.17]:
署名
<function>名、パラメータータイプリスト(8.3.5)、および囲んでいる名前空間(存在する場合)
[注:署名は、名前のマングリングとリンクの基礎として使用されます。 —エンドノート]
[C++11: 8.3.5/6]:
[..]戻り値の型、parameter-type-list、ref-qualifier、およびcv -qualifier-seqですが、デフォルトの引数(8.3.6)または例外仕様(15.4)は、関数型の一部ではありません。 [注:関数タイプは、関数へのポインター、関数への参照、およびメンバー関数へのポインターの割り当てと初期化中にチェックされます。 —エンドノート]
これらは本質的に、コンパイラーが使用する関数の宣言を確認できることによって「アクティブ化」され、関数呼び出しの時点で挿入される構文糖衣です。
#include <iostream>
void foo(int x = 5)
{
std::cout << x << '\n';
}
int main()
{
foo();
}
5
デフォルトの引数は「表示」です。
ただし、関数をポインターの後ろに「隠す」と、次のようになります。
int main()
{
void (*bar)(int) = &foo;
bar();
}
too few arguments to function
bar
のタイプは正しく、コンパイラーはfoo
にデフォルトがあることを認識していますが、コンパイラーに通知するためのdirect構文は存在しません。 bar
を呼び出すとbar
もfoo
になります。確かに、この些細なシナリオでは、割り当てを観察することでそれを理解することができますが、それはより広い議論を正当化することはほとんどありません。
同じ理由で、呼び出しサイトに表示されない定義でのみ記述されているデフォルトの引数はほとんど役に立ちません。
// a.h
void foo(int);
// a.cpp
#include "a.h"
#include <iostream>
void foo(int x = 5)
{
std::cout << x << '\n';
}
// main.cpp
#include "a.h"
int main()
{
foo();
}
too few arguments to function
これが理由だと思います:
[C++11: 8.3.6/4]:
[..]異なるスコープの宣言には、完全に異なるデフォルト引数のセットがあります。 [..]
areクラスの非テンプレートメンバー関数定義のデフォルト引数を「積み上げる」ことができます([C++11 8.3.6/6]
);この例は、このデフォルトが同じTUにのみ適用されることを示しています。これは、上記の2番目のコードスニペットで見た動作に従います。
したがって、デフォルトの引数が関数型の一部ではなく、呼び出しサイトに明確に表示される必要がある場合、ラムダに役立つ、些細な工夫が施されたコーナーケースはほんの一握りです。それらが作成されたのと同じスコープで、コンパイラーがラムダへの呼び出しのデフォルト引数を「入力」する方法を簡単に理解できるようにします。それで、そのポイントは何ですか?それほど多くはありません、私はあなたに言います。