C++14:具有泛型 std::function 作为 class 成员的泛型 lambda

C++14: Generic lambda with generic std::function as class member

考虑这个伪片段:

class SomeClass
{
public:
    SomeClass()
    {
        if(true)
        {
            fooCall = [](auto a){ cout << a.sayHello(); };
        }
        else
        {
            fooCall = [](auto b){ cout << b.sayHello(); };
        }
    }
private:
    template<typename T>
    std::function<void(T)> fooCall;
};

我想要的是一个 class 成员 fooCall,它存储一个通用的 lambda,它又在构造函数中赋值。

编译器抱怨 fooCall 不能成为模板化数据成员。

有没有关于如何在 class 中存储通用 lambda 的简单解决方案?

您无法在 运行 时间在两个通用 lambda 之间进行选择,因为您没有具体的签名来进行类型擦除。

如果您可以在编译时做出决定,您可以将 class 本身模板化:

template <typename F>
class SomeClass
{
private:
    F fooCall;

public:
    SomeClass(F&& f) : fooCall{std::move(f)} { }
};

然后您可以创建一个辅助函数来推断 F:

auto makeSomeClassImpl(std::true_type) 
{
    auto l = [](auto a){ cout << a.sayHello(); };
    return SomeClass<decltype(l)>{std::move(l)};
}

auto makeSomeClassImpl(std::false_type) 
{
    auto l = [](auto b){ cout << b.sayHello(); };
    return SomeClass<decltype(l)>{std::move(l)};
}

template <bool B>
auto makeSomeClass() 
{
    return makeSomeClassImpl(std::bool_constant<B>{});
}

我无法将 std::function<> 作为 generic lambda 直接存储在 class 中作为 member。我能够做的是在 class 的构造函数中专门使用一个。我不是 100% 确定这是否是 OP 试图实现的目标,但这是我能够编译、构建和 运行 的目标,我怀疑 OP 是通过他们提供的代码瞄准的.

template<class>
class test {
public: // While testing I changed this to public access...
        // Could not get object below to compile, build & run
    /*template<class U = T>
    static std::function<void(U)> fooCall;*/
public:
   test();
};

template<class T>
test<T>::test() {
    // This would not compile, build & run
    // fooCall<T> = []( T t ) { std::cout << t.sayHello(); };

    // Removed the variable within the class as a member and moved it here
    // to local scope of the class's constructor
    std::function<void(T)> fooCall = []( auto a ) { std::cout << a.sayHello(); };
    T t; // created an instance of <Type T>
    fooCall(t); // passed t into fooCall's constructor to invoke the call.
}

struct A {
    std::string sayHello() { return "A say's Hello!\n"; }
};

struct B {
    std::string sayHello() { return "B say's Hello!\n"; }
};


int main() {
    // could not instantiate an object of SomeClass<T> with a member of
    // a std::function<> type that is stored by a type of a generic lambda.

    /*SomeClass<A> someA;
    SomeClass<B> someB;
    someA.foo();
    someB.foo();*/

    // Simply just used the object's constructors to invoke the locally stored lambda within the class's constructor.
    test<A> a;
    test<B> b;

    std::cout << "\nPress any key & enter to quit." << std::endl;
    char c;
    std::cin >> c;

    return 0;
}

使用适当的 headers 以上应该编译、构建 & 运行 给出下面的输出(至少在 Windows 7 64 位的 MSVS 2017 中做到了);我在我 运行 出错的地方留下了评论,并尝试了多种不同的技术来实现一个工作示例,错误发生了其他人的建议,我在使用上述代码时发现了更多错误。我能够编译、构建和 运行 的内容归结为这里没有注释的这段简单代码。我还添加了另一个简单的 class 来表明它适用于任何类型:

template<class>
class test {
public:
    test();
};

template<class T>
test<T>::test() {
    std::function<void( T )> fooCall = []( auto a ) { std::cout << a.sayHello(); };
    T t;
    fooCall( t );
}

struct A {
    std::string sayHello() { return "A say's Hello!\n"; }
};

struct B {
    std::string sayHello() { return "B say's Hello!\n"; }
};

struct C {    
    int sayHello() { return 100; }
};

int main() {
    test<A> testA;
    test<B> testB;
    test<C> testC;

    std::cout << "\nPress any key & enter to quit." << std::endl;
    char c;
    std::cin >> c;

    return 0;
}

输出:

A say's Hello!
B say's Hello!
100

Press any key & enter to quit

我不知道这是否会直接或间接地帮助 OP,但如果有帮助,或者即使没有帮助,他们仍然可能会回来并以此为基础。

您可以简单地使用模板 class 或...
如果你可以使用 c++17,你可以制作 fooCall 的类型
std::function<void(const std::any&)> 并制作一个小包装器来执行它。

方法 1:简单地使用模板 class (C++14).
方法 2:似乎完全按照 OP 的意图模仿伪代码 (C++17)。
方法 3 : 比方法 2 (C++17) 更简单易用。
方法 4:允许我们更改 fooCall (C++17) 的值。

  • 演示需要 headers 和测试结构:
#include <any> //not required for method 1
#include <string>
#include <utility>
#include <iostream>
#include <functional>

struct typeA {
    constexpr const char * sayHello() const { return "Hello from A\n"; }
};

struct typeB {
    const std::string sayHello() const { return std::string(std::move("Hello from B\n")); }
};
  • 方法一:
template <typename T>
class C {
    const std::function<void(const T&)> fooCall;
public:
    C(): fooCall(std::move([](const T &a) { std::cout << a.sayHello(); })){}

    void execFooCall(const T &arg) {
        fooCall(arg);
    }
};

int main (void) {
    typeA A;
    typeB B;
    C<typeA> c1;
    C<typeB> c2;
    c1.execFooCall(A);
    c2.execFooCall(B);
    return 0;
}
  • 方法二:
bool is_true = true;

class C {
    std::function<void(const std::any&)> fooCall;
public:
    C() {
        if (is_true)
            fooCall = [](const std::any &a) { std::cout << std::any_cast<typeA>(a).sayHello(); };
        else
            fooCall = [](const std::any &a) { std::cout << std::any_cast<typeB>(a).sayHello(); };
    }
    template <typename T>
    void execFooCall(const T &arg) {
        fooCall(std::make_any<const T&>(arg));
    }
};

int main (void) {
    typeA A;
    typeB B;
    C c1;
    is_true = false;
    C c2;
    c1.execFooCall(A);
    c2.execFooCall(B);
    return 0;
}
  • 方法三:
/*Note that this very closely resembles method 1. However, we're going to 
  build off of this method for method 4 using std::any*/
template <typename T>
class C {
    const std::function<void(const std::any&)> fooCall;
public:
    C() : fooCall(std::move([](const std::any &a) { std::cout << std::any_cast<T>(a).sayHello(); })) {}

    void execFooCall(const T &arg) {
        fooCall(std::make_any<const T&>(arg));
    }
};

int main (void) {
    typeA A;
    typeB B;
    C<typeA> c1;
    C<typeB> c2;
    c1.execFooCall(A);
    c2.execFooCall(B);
    return 0;
}
  • 方法四:
/*by setting fooCall outside of the constructor we can make C a regular class 
  instead of a templated one, this also complies with the rule of zero.
  Now, we can change the value of fooCall whenever we want.
  This will also allow us to do things like create a container that stores
  a vector or map of functions that each take different parameter types*/
class C {
    std::function<void(const std::any&)> fooCall; //could easily be replaced by a vector or map
public:
    /*could easily adapt this to take a function as a parameter so we can change
      the entire body of the function*/
    template<typename T>
    void setFooCall() {
        fooCall = [](const std::any &a) { std::cout << std::any_cast<T>(a).sayHello(); };
    }

    template <typename T>
    void execFooCall(const T &arg) {
            fooCall(std::make_any<const T&>(arg));
    }
};

int main (void) {
    typeA A;
    typeB B;
    C c;
    c.setFooCall<typeA>;
    c.execFooCall(A);
    c.setFooCall<typeB>;
    c.execFooCall(B);
    return 0;
}
  • 任何方法的输出
Hello from A
Hello from B