簡體   English   中英

std :: bind與可變參數模板函數

[英]std::bind with variadic template function

我正在嘗試使用variadic模板編寫一個泛型obj工廠來調用各種類的構造函數。 代碼如下:

#include <iostream>
#include <string>
#include <memory>
#include <functional>
#include <unordered_map>

template<typename T>
class ObjFactory {
public:
    typedef std::shared_ptr<T>              ObjPtr;
    typedef std::function<ObjPtr(void)>     CreatorFunc;
public:
    void registerCreator(const std::string &name, const CreatorFunc &creator)
    { m_dictCreator[name] = creator; }

    ObjPtr getObj(const std::string &name) const
    {
        auto it = m_dictCreator.find(name);
        return (it == m_dictCreator.end() ? nullptr : (it->second)());
    }
private:
    std::unordered_map<std::string, CreatorFunc>   m_dictCreator;
};


using namespace std;

struct Base {
    virtual ~Base() {}
    virtual void greet() const
    { cout << "I am Base" << endl; }
};

struct Derived : Base {
    Derived() : x(0), y(0) {}
    Derived(int a, int b) : x(a), y(b) {}
    int x, y;

    virtual void greet() const
    { cout << "I am Derived x = " << x << " y = " << y << endl; }
};

template<typename T, typename... Args>
std::shared_ptr<T> create_obj(Args... args)  // This OK
// std::shared_ptr<T> create_obj(Args&&... args) // WRONG
{ return std::make_shared<T>(std::forward<Args>(args)...); }

int main()
{
    ObjFactory<Base> factory;
    factory.registerCreator("default", create_obj<Derived>);
    factory.registerCreator("withArgs", std::bind(create_obj<Derived, int, int>, 1, 2));

    do {
        auto pObj = factory.getObj("default1");
        if (pObj) { pObj->greet(); }
    } while (0);

    do {
        auto pObj = factory.getObj("withArgs");
        if (pObj) { pObj->greet(); }
    } while (0);

    return 0;
}

在大多數示例中,variadic arg總是在函數arg列表中寫出這樣的“Args && ...”。 但這不適用於bind,編譯錯誤msg就像這樣(clang-902.0.39.2)

錯誤:沒有可行的轉換從'__bind(&)(int &&,int &&),int,int>'到'const ObjFactory :: CreatorFunc'(又名'const function()>')factory.registerCreator(“withArgs”, std :: bind(create_obj,1,2));

刪除“&&”后,它可以正常工作

但我不知道為什么?

通用引用僅適用於推斷的上下文。 當明確指定模板參數時,它們不會按預期運行。

給出功能模板

template <typename... Args>
void foo(Args&&... args) {}

和電話

int a = 1;
int b = 2;
foo(a, b);

Args將被推斷為{int&, int&} 應用了引用折疊, int& &&折疊為int& 這意味着args中值的類型是{int&, int&}

如果用參數的rvalues(即foo(1, 2) )調用它,那么Args將被推導為{int, int}args中的值的類型變為{int&&, int&&}


這是通用引用的基礎知識,所以現在讓我們來看看你打電話時會發生什么

auto fn = std::bind(foo<int, int>, 1, 2);
fn();

在這里,你不允許發生模板參數推斷,因此Args{int, int} ,因此foo期望類型為{int&&, int&&} 12將復制到綁定對象中,並作為lvalues傳遞給callable。 Rvalue引用不能綁定到左值,因此調用無法編譯。


使其正常工作的方法是使用lambda而不是std::bind

auto fn = []() { foo(1, 2); };
fn();

對於lambda,模板參數推導工作正常, 12仍然是rvalues。 一切都按預期工作,通用引用完成它們的工作,因為它們被用於推斷的上下文中。

當使用轉發引用時,你需要讓參數被推斷,否則你不能完美地轉發它們,因為你不會知道它們使用的真實類型。

在你的情況下你給了一個類型create_obj<Derived, int, int>這個函數將被實例化為std::shared_ptr<Derived> create_obj(int&&, int&&)並且沒有靈活性它只會采用r-value int。

並且你將callable分配給const CreatorFunc&因此閉包是const而你的callable不能接收const參數

通過create_obj<Derived, const int&, const int&>替換create_obj<Derived, int, int> create_obj<Derived, const int&, const int&>導致create_obj被實例化為std::shared_ptr<Derived> create_obj(const int&, const int&)將在這種情況下起作用但仍然會沒有轉發引用的靈活性。

真正的解決方案是使用lambda。

暫無
暫無

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

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