[英]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&&}
。 值1
和2
將復制到綁定對象中,並作為lvalues傳遞給callable。 Rvalue引用不能綁定到左值,因此調用無法編譯。
使其正常工作的方法是使用lambda而不是std::bind
:
auto fn = []() { foo(1, 2); };
fn();
對於lambda,模板參數推導工作正常, 1
和2
仍然是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.