直感的なレベルでは、(参照などを介して)状態を実行する必要がないラムダは、ネイキッド関数ポインターに完全に変換できる必要があります。しかし、最近、GCC、Clang、およびMSVCで次のエラーが発生するのを見て驚いた:
int main(int, char *[]) {
void (*fp)() = []{}; // OK
//fp = [=]{}; // XXX - no user defined conversion operator available
//fp = [&]{}; // XXX - same ...
}
C++ 17仕様(または少なくとも 目に見える公開ドラフトバージョンN471 )は、§8.4.5.1の項目7[expr.prim.lambda.closure]キャプチャありとキャプチャなしのラムダ:
非ジェネリックラムダ式のクロージャタイプラムダキャプチャなしの制約(存在する場合)が満たされ、同じパラメータを持つC++言語リンケージ(10.5)を使用する関数へのポインタへの変換関数があるクロージャー型の関数呼び出し演算子として型を返します。 ...
ただし、正式な文法を調べると、§8.4.5[expr.prim.lambda]に次のように表示されます。
- lambda-expression:
- lambda-introducercompound-statement
- ...
- lambda-introducer:
- [lambda-captureopt]
- ...
および8.4.5.2で[expr.prim.lambda.capture]:
- lambda-capture:
- capture-default
- キャプチャリスト
- capture-default、capture-list
- capture-default:
- &
- =
したがって、すべての編集者は実際に私の落胆に対する法律の手紙に従っていました...
ボディが静的/キャプチャされていない状態への参照を含むかどうかに基づくのではなく、言語がキャプチャの存在を宣言の狭い文法上の違いとして定義するのはなぜですか?
改宗を認めた変化は、国家機関のコメントによって始められました。 n3052:ラムダを関数ポインタに変換する を参照してください。これは、国家機関を指します コメントUK 42 :
空のキャプチャリストを持つラムダは、通常の関数型と同じセマンティクスを持っています。このマッピングを要求することにより、既存のオペレーティングシステムおよびCライブラリ関数とも互換性のある既知のAPIを備えた効率的なラムダ型を取得します。
N3052
の解決策は次のとおりです。
解決策:新しい段落を追加します。「空のキャプチャセットを持つラムダ式は、関数型R(P)へのポインターに変換可能でなければなりません。Rは戻り型で、Pはラムダ式のパラメーター型リストです。」さらに、(a)関数参照への変換を許可し、(b)extern "C"関数ポインタ型を許可するのも良い方法です。
...
段落5の後に新しい段落を追加します。この編集の目的は、ラムダキャプチャのないラムダのクロージャーから関数ポインターへの変換を取得することです。
ラムダキャプチャのないラムダ式のクロージャ型には、クロージャ型の関数呼び出し演算子と同じパラメータと戻り型を持つ関数へのポインタへのパブリック非仮想非明示const変換関数があります。この変換関数によって返される値は、呼び出されたときに、クロージャタイプの関数呼び出し演算子を呼び出すのと同じ効果を持つ関数のアドレスになります。
それが今日の私たちのところです。コメントには空のキャプチャリストとあり、今日の内容はコメントに記載されている意図と一致しているようです。
国家機関のコメントに基づく修正であり、狭く適用されたようです。
あなたが提案するルールは、特に暗黙のキャプチャがodr-useに依存しているP0588R1より前の世界では、非常に壊れやすいでしょう。
検討してください:
void g(int);
void h(const int&);
int my_min(int, int);
void f(int i) {
const int x = 1, y = i;
[=]{ g(x); }; // no capture, can convert?
[=]{ g(y); }; // captures y
[=]{ h(x); }; // captures x
[=]{ my_min(x, 0); }; // no capture, can convert?
[=]{ std::min(x, 0); }; // captures x
}