ラムダ式を返す関数


88

C ++ 11でラムダ関数を返す関数を書くことは可能だろうか。もちろん、1つの問題は、そのような関数をどのように宣言するかです。各ラムダには型がありますが、その型はC ++では表現できません。私はこれがうまくいくとは思いません:

auto retFun() -> decltype ([](int x) -> int)
{
    return [](int x) { return x; }
}

これも:

int(int) retFun();

ラムダから関数へのポインターなどへの自動変換については知りません。関数オブジェクトを手作りして返す唯一の解決策はありますか?


1
すでに述べられていることを付け加えると、ステートレスラムダ関数は関数ポインターに変換可能です。
snk_kid

3
IMOの最初のオプションはdecltype機能本体のラムダと同じではないため機能しないため、(returnステートメントを含めても)タイプが異なります
Motti

1
ちなみに、ラムダに空のキャプチャ句がある場合、関数へのポインタに暗黙的に変換できます。
GManNickG 2011年

@GMan:Visual C ++ 2010または約1年以上前(またはその前後)にリリースされたバージョンのg ++​​を使用している場合を除きます。関数ポインタへのキャプチャレスラムダの暗黙的な変換は、N3092で2010年3月まで追加されませんでした。
James McNellis、2011年

4
ラムダ式は一般に、未評価のオペランドには使用できません。つまりdecltype([](){})sizeof([]() {})どこに書いても、形式が正しくありません。
Johannes Schaub-litb

回答:


98

手作りの関数オブジェクトは必要ありませんstd::function。ラムダ関数が変換可能なを使用するだけです。

この例では、整数識別関数を返します。

std::function<int (int)> retFun() {
    return [](int x) { return x; };
}

6
ああ!StackOverflowが大好きです。トピックを調査してからStackOverflowで答えを得るには、ずっと時間がかかっただろう。ショーン、ありがとう!
Bartosz Milewski、2011年

5
これにより、のコンストラクタでメモリ割り当てが発生しますstd::function
Maxim Egorushkin、2011年

2
@Maxim Yegorushkin std :: functionには移動セマンティクスがあり、カスタムアロケーターを使用できます。C++ 0xワーキングドラフトには次のメモがあります:「[注:小さな呼び出し可能オブジェクトに動的に割り当てられたメモリを使用しないようにすることをお勧めします。 、ここでfのターゲットは、オブジェクトへのポインターまたは参照とメンバー関数ポインターのみを保持するオブジェクトです。—end note] "なので、基本的に、特定の実装がどの割り当て戦略を使用しているかについて多くの仮定を立てることはできませんが、とにかく、独自の(プールされた)アロケーターを使用します。
snk_kid

1
@Maxim:私の答えは「関数オブジェクトを手作りしてそれを返す唯一のソリューションは何か?」という質問への回答でした。
Sean

2
std::functionは型消去を採用していることを覚えておいてください。これは、を呼び出すときに仮想関数を呼び出すコストを意味しますstd::function。返された関数がタイトな内部ループまたはわずかな非効率性が問題となる他のコンテキストで使用される場合に注意する必要があるもの。
アンソニーホール

29

この簡単な例では、は必要ありませんstd::function

標準§5.1.2/ 6から:

以下のための閉鎖型のラムダ式なしでラムダ・キャプチャーは、閉鎖型の関数呼び出し演算子と同じパラメータと戻り値の型を持つ関数へのポインタへの公共非仮想非明示的なconstの変換機能を持っています。この変換関数によって返される値は、呼び出されたときに、クロージャタイプの関数呼び出し演算子を呼び出すのと同じ効果を持つ関数のアドレスになります。

関数にはキャプチャがないため、ラムダを型の関数へのポインターに変換できることを意味しますint (*)(int)

typedef int (*identity_t)(int); // works with gcc
identity_t retFun() { 
  return [](int x) { return x; };
}

それは私の理解です、私が間違っているなら私を訂正してください。


1
これは正しいですね。残念ながら、私が使用している現在のコンパイラでは動作しません:VS2010。std:: function変換が機能します。
Bartosz Milewski、2011年

3
はい、この規則の最後の表現はVC2010には遅すぎました。
Ben Voigt

コード例を追加しました。ここに完全なプログラムがあります。
jfs

@JFSebastian-この例のラムダの寿命はどれくらいですか?関数ポインターへの変換の結果を長持ちさせるのに十分な長さですか?
Flexo

1
: - @JFSebastian私は独自の権利で質問として、それを求めてきましたので、私はその答えを見つけることができないようstackoverflow.com/questions/8026170/...
フレキソ

21

ラムダ関数の戻り値の型を明示的に指定する必要がないため、他のラムダ関数からラムダ関数を返すことができます。そのようなものをグローバルスコープで書くだけです:

 auto retFun = []() {
     return [](int x) {return x;};
 };

2
これは、外側のラムダがreturnステートメントのみで構成されている場合にのみ当てはまります。それ以外の場合は、戻り値の型を指定する必要があります。
Bartosz Milewski、2011

3
std :: functionの実行時のポリモーフィズムを必要とせず、ラムダが空でないキャプチャリストを持つことができるため、これが最良の答えです。ただし、const auto fun = ...
robson3.14

2
C ++ 14以降、@ BartoszMilewskiは真ではありません。
dzhioev

19

質問は特にC ++ 11について尋ねていますが、これに出くわしてC ++ 14コンパイラにアクセスできる他の人のために、C ++ 14は通常の関数の推定戻り型を許可するようになりました。したがって、質問の例は-> decltype、関数パラメーターリストの後に...句を削除するだけで、希望どおりに機能するように調整できます。

auto retFun()
{
    return [](int x) { return x; }
}

ただし、return <lambda>;関数に複数表示されている場合は機能しません。これは、戻り値の型の推論に関する制限は、すべてのreturnステートメントが同じ型の式を返す必要があることですが、すべてのラムダオブジェクトにはコンパイラーによって独自の一意の型が与えられるため、return <lambda>;式はそれぞれ異なる型になるためです。


4
なぜc ++ 14の推定型について言及するが、多相ラムダを省略するのですか auto retFun() { return [](auto const& x) { return x; }; }
2016年

1

あなたはこのように書くべきです:

auto returnFunction = [](int x){
    return [&x](){
        return x;
    }();
};

関数として戻り値を取得し、次のように使用します。

int val = returnFunction(someNumber);

0

c ++ 11がなく、マイクロコントローラーなどでc ++コードを実行している場合。voidポインターを返し、キャストを実行できます。

void* functionThatReturnsLambda()
{
    void(*someMethod)();

    // your lambda
    someMethod = []() {

        // code of lambda

    };

    return someMethod;
}


int main(int argc, char* argv[])
{

    void* myLambdaRaw = functionThatReturnsLambda();

    // cast it
    auto myLambda = (void(*)())myLambdaRaw;

    // execute lambda
    myLambda();
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.