C++14:具有泛型std::函数作为类成员的泛型lambda

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

本文关键字:泛型 成员 lambda 函数 std C++14      更新时间:2023-10-16

考虑这个伪片段:

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;
};

我想要的是一个类成员fooCall,它存储一个泛型lambda,然后在构造函数中分配它。

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

关于如何在类中存储泛型lambda,有什么简单的解决方案吗?

在运行时,您无法在两个通用Lambda之间进行选择,因为您没有要键入擦除的具体签名。

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

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直接作为member存储在类中。我所能做的就是在类的构造函数中专门使用一个。我不能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;
}

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

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,但如果它真的有帮助,或者即使没有帮助,他们仍然可以回来并以此为基础。

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

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

  • 演示所需的标题和测试结构:
#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 An"; }
};
struct typeB {
const std::string sayHello() const { return std::string(std::move("Hello from Bn")); }
};
  • 方法1:
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;
}
  • 方法2:
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;
}
  • 方法3:
/*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;
}
  • 方法4:
/*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;
}
  • Any方法的输出
Hello from A
Hello from B