21

関数からファンクターを作成する方法が必要です。今、関数呼び出しをラムダ関数でラップし、後でインスタンス化しようとしています。しかし、コンパイラーは、ラムダコンストラクターが削除されていると言っています。このコードをコンパイルする方法はありますか? それともそのための別の方法ですか?

#include <iostream>  

void func()
{
    std::cout << "Hello";
}

auto t = []{ func(); };
typedef decltype(t) functor_type;

template <class F>
void functor_caller()
{
    F f;
    f();
}

int main()
{
    functor_caller<functor_type>();
    return 0;
}

今、私はそのようなコンパイラエラーを受け取ります:

error: use of deleted function  '<lambda()>::<lambda>()'

error: a lambda closure type has a deleted default constructor

私の意見では、唯一の方法はマクロを使用することです:

#define WRAP_FUNC(f) \
struct f##_functor       \
{                       \
    template <class... Args >                             \
    auto operator()(Args ... args) ->decltype(f(args...)) \
    {                                                     \
        return f(args...);                                \
    }                                                     \
};

それから

WRAP_FUNC(func);

そして(主に)

functor_caller<func_functor>()
4

4 に答える 4

8

コードの意味がありません。次のようなキャプチャ ラムダがあるとします。

{
    int n = 0;
    auto t = [&n](int a) -> int { return n += a; };
}

タイプのオブジェクトをデフォルトで構築することは、どういう意味decltype(t)でしょうか?

function@Matthieu が示唆するように、ラムダをオブジェクトにラップできます。

std::function<int(int)> F = t;

または、ラムダ (または任意の呼び出し可能なエンティティ) の型で呼び出しサイトを直接テンプレート化できます。

template <typename F>
int compute(int a, int b, F f)
{
    return a * f(b);  // example
}

使用法:int a = 0; for (int i : { 1, 3, 5 }) { a += compute(10, i, t); }

可能な限り、2 番目のスタイルが推奨std::functionされます。これは、結果のオブジェクトを介した実際の関数呼び出しと同様に、への変換が自明ではなく、コストがかかる可能性がある操作だからです。ただし、異種の呼び出し可能なエンティティの統一されたコレクションを格納する必要がある場合はstd::function、最も簡単で便利なソリューションになる可能性があります。

于 2012-06-17T19:35:57.607 に答える
7

ラムダは常に空であると想定できます。したがって、両方とも同じメモリ レイアウトを持っているため、別の空の型からキャストすることができます。そこで、デフォルトの構築可能な関数オブジェクトを作成するラッパー クラスを構築します。

template<class F>
struct wrapper
{
    static_assert(std::is_empty<F>(), "Lambdas must be empty");
    template<class... Ts>
    auto operator()(Ts&&... xs) const -> decltype(reinterpret_cast<const F&>(*this)(std::forward<Ts>(xs)...))
    {
        return reinterpret_cast<const F&>(*this)(std::forward<Ts>(xs)...);
    }
};

ラムダが常に空であることを確認するために、静的アサートが追加されました。これは常に当てはまるはずですが (関数ポインターに減衰する必要があるため)、標準では明示的に保証されていません。したがって、アサートを使用して、少なくともラムダの非常識な実装をキャッチします。次に、両方とも空であるため、ラッパー クラスをラムダにキャストするだけです。

最後に、ラムダは次のように構築できます。

template <class F>
void function_caller()
{
    wrapper<F> f;
    f();
}
于 2014-09-30T23:57:15.470 に答える
2

いいえ。

ただし、ラムダはコピーできると信じているfunctor_callerため、引数を使用してその属性を初期化できます。

それでも、車輪を再発明する代わりに、代わりに使用std::functionします.

于 2012-06-17T17:01:32.673 に答える