[解決済み] ラムダが自分自身を返す:これは合法か?
質問
このかなり無駄なプログラムを考えてみましょう。
#include <iostream>
int main(int argc, char* argv[]) {
int a = 5;
auto it = [&](auto self) {
return [&](auto b) {
std::cout << (a + b) << std::endl;
return self(self);
};
};
it(it)(4)(6)(42)(77)(999);
}
基本的には自分自身を返すラムダを作ろうとしています。
- MSVCがプログラムをコンパイルし、実行されます。
- gcc はプログラムをコンパイルし、セグメンテーションを行います。
-
clangはプログラムを拒否し、メッセージを表示します。
error: function 'operator()<(lambda at lam.cpp:6:13)>' with deduced return type cannot be used before it is defined
どのコンパイラが正しいのでしょうか?静的制約違反なのか、UBなのか、それともどちらでもないのか?
更新 このわずかな修正はclangによって受理されます。
auto it = [&](auto& self, auto b) {
std::cout << (a + b) << std::endl;
return [&](auto p) { return self(self,p); };
};
it(it,4)(6)(42)(77)(999);
アップデート2 : 自分自身を返すファンクタの書き方や、Yコンビネータを使えば実現できることは理解できました。これはどちらかというと言語学者的な質問です。
アップデート3 : 問題は ではなく ラムダが自分自身を返すことが一般的に合法であるかどうかではなく、これを行うこの特定の方法の合法性についてです。
関連する質問 C++のラムダが自分自身を返す .
どのように解決するのですか?
プログラムが不正です(clangが正しい)。 [dcl.spec.auto]/9 :
式中に非推奨のプレースホルダ型を持つ実体の名前が現れると、そのプログラムは不正な形式となる。しかし、一旦関数内で非開示の return 文が見られると、その文から推測される return 型は、他の return 文を含む関数の残りの部分で使用することができます。
基本的に、内側のラムダの戻り値の型の推論はそれ自身に依存する(ここで名前を付けられている実体は呼び出し演算子である)。このケースでは、内側のラムダの型が必要だが、それを指定することができないので、それは不可能だ。しかし、このように再帰的なラムダを強制することで、うまくいくケースもあります。
それでなくとも、あなたは ダングリングリファレンス .
もっと賢い人 (つまり T.C.) と議論した後で、もう少し詳しく説明させてください。元のコード (少し縮小) と提案された新しいバージョン (同様に縮小) の間に重要な違いがあります。
auto f1 = [&](auto& self) {
return [&](auto) { return self(self); } /* #1 */ ; /* #2 */
};
f1(f1)(0);
auto f2 = [&](auto& self, auto) {
return [&](auto p) { return self(self,p); };
};
f2(f2, 0);
ということで、内側の式である
self(self)
には依存しません。
f1
には依存しませんが
self(self, p)
は
f2
. 式が非依存的であるとき、それらは...熱心に使用することができます (
[temp.res]/8
は,例えば,どのように
static_assert(false)
は、それ自身を見つけたテンプレートがインスタンス化されているかどうかに関係なく、ハードエラーであることを意味します)。
については
f1
に対して、コンパイラ(例えばclang)はこれをeagerlyにインスタンス化しようとすることができます。外側のラムダの型は、一旦
;
の時点で
#2
上記のように(内側のラムダの型です)、それより前に使おうとしているのですが、(点であると考えて
#1
の時点と考える)、つまり、内側のラムダをまだパースしている間に使おうとしているのです。これは、dcl.spec.auto/9 に反しています。
しかし
f2
については、依存性があるため、eagerly にインスタンス化することはできません。私たちは使用時にのみインスタンス化することができ、その時点ではすべてを知っています。
このようなことを実際に行うためには y-コンビネータ . 論文にあった実装です。
template<class Fun> class y_combinator_result { Fun fun_; public: template<class T> explicit y_combinator_result(T &&fun): fun_(std::forward<T>(fun)) {} template<class ...Args> decltype(auto) operator()(Args &&...args) { return fun_(std::ref(*this), std::forward<Args>(args)...); } }; template<class Fun> decltype(auto) y_combinator(Fun &&fun) { return y_combinator_result<std::decay_t<Fun>>(std::forward<Fun>(fun)); }
そして、あなたが望むものは
auto it = y_combinator([&](auto self, auto b){
std::cout << (a + b) << std::endl;
return self;
});
関連
-
[解決済み】LLVMで暗黙のうちに削除されたコピーコンストラクタの呼び出し
-
[解決済み] 非静的データメンバの無効な使用
-
[解決済み】クラスのコンストラクタへの未定義参照、.cppファイルの修正も含む
-
[解決済み】演算子のオーバーロード C++; <<操作のパラメータが多すぎる
-
[解決済み] なぜ、オブジェクトそのものではなく、ポインタを使用しなければならないのですか?
-
[解決済み] リスト内包とラムダ+フィルタの比較
-
[解決済み] クロージャ」と「ラムダ」の違いは何ですか?
-
[解決済み] Distinct() with lambda?
-
[解決済み] ラムダ(関数)とは何ですか?
-
[解決済み】C++11のラムダ式って何?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】coutはstdのメンバではない
-
[解決済み】C-stringを使用すると警告が表示される。"ローカル変数に関連するスタックメモリのアドレスが返される"
-
[解決済み] [Solved] Error C1083: Cannot open include file: 'stdafx.h'
-
[解決済み】クラステンプレートの使用にはテンプレート引数リストが必要です
-
[解決済み] [Solved] インクルードファイルが開けません。'stdio.h' - Visual Studio Community 2017 - C++ Error
-
[解決済み】CMakeエラー at CMakeLists.txt:30 (project)。CMAKE_C_COMPILER が見つかりませんでした。
-
[解決済み] to_string は std のメンバーではない、と g++ が言っている (mingw)
-
[解決済み】システムが指定されたファイルを見つけられませんでした。
-
[解決済み] 配列のベクトルを扱う正しい方法
-
[解決済み】'std::cout'への未定義の参照