簡體   English   中英

C++14:具有泛型 std::function 作為類成員的泛型 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;
};

我想要的是一個類成員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;
}

使用適當的頭文件,上面應該編譯、構建和運行給出下面的輸出(至少在 Windows 7 64 位的 MSVS 2017 中做到了); 我在遇到錯誤的地方留下了評論,並嘗試了多種不同的技術來實現一個工作示例,錯誤發生在其他人的建議中,我在使用上述代碼時發現了更多錯誤。 我能夠編譯、構建和運行的內容歸結為這里沒有注釋的這段簡單代碼。 我還添加了另一個簡單的類來顯示它適用於任何類型:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM