我有以下模板方法:
template<typename T, typename... Args>
void register_scene(const std::string& name, Args&&... args) {
auto func = std::bind(&T::template create<Window*, Args&...>, std::placeholders::_1, std::forward<Args>(args)...);
_store_scene_factory(name, [=](Window* window) -> SceneBasePtr {
auto ret = func(window);
ret->set_name(name);
return ret;
});
}
基本上我需要做的就是将可变参数Args
绑定到T::create
(它本身是一个静态的可变参数模板方法),但允许在第一个参数(窗口)单独填充时将其填充。被称为。
上述代码失败,出现以下错误
error: no match for call to ‘(const std::_Bind<std::shared_ptr<{anonymous}::SceneWithArgs> (*(std::_Placeholder<1>, const char*))(smlt::Window*&, const char (&)[4])>) (smlt::Window*&)’
auto ret = func(window);
~~~~^~~~~~~~
调用这样的代码时:
manager.register_scene<SceneWithArgs>("test", "arg");
我真的不了解错误或如何解决错误。
我最初通过简单地在lambda中调用create来解决这个问题,这适用于GCC 4.9及更高版本,但是我必须保持与GCC 4.8.4兼容,并且有一个错误阻止在lambda中使用variadic args :(
更新
好的,所以添加std :: decay(如评论中所示)并没有完全解决问题,第一个参数不断推导到Window*&&
而不是Window*
,但实际指定了func
的类型(例如std::function<SceneBasePtr (Window*)>
),而不是使用auto
编译的东西。
我不确定为什么会这样......
答案 0 :(得分:2)
您没有显示create
的声明,但我认为它看起来如下:
template <typename... Args>
static SceneBasePtr create(Args&&...);
看起来不错。您使用转发引用,以便编译器可以推断出确切的参数类型。只要编译器推断出类型,这就行了。然而...
&T::template create<Window*, Args&...>
在这里,您明确地实例化函数模板。也就是说,编译器将不再推断类型,而是通过您提供的模板参数替换模板参数。这些模板参数是什么?
template <typename T, typename... Args>
void register_scene(const std::string& name, Args&&... args);
//...
manager.register_scene<SceneWithArgs>("test", "arg");
第一个是明确传递的,它是Window*
,这个很明显。现在Args...
- "test"
和"arg"
是原始字符串文字,其对应的类型分别为const char[5]
和const char[4]
。然后Args...
(扣除流程后)变为const char(&)[5]
和const char(&)[4]
。那么当你用这些类型实例化create
时会发生什么?您最终会得到如下声明:
static SceneBasePtr create(Window*&&, const char(&)[5], const char(&)[4]);
请注意,引用折叠规则会将第一个参数转换为r值引用,而其余参数则为l值引用。
std::bind
也会推断出类型。但是,std::bind
将要存储参数,以便以后可以重用它们。问题是std::bind
无法存储const char[5]
。相反,它将衰减每个参数的类型。这意味着每个原始字符串文字将变为const char*
,并将作为此类型的l值参数传递给绑定函数,这与手动实例化的create
不匹配。类似的问题是window
参数。它被推导为l值,但是,手动实例化的create
函数需要r值。
最好不要明确指定函数模板的模板类型。如果由于某些原因(bug?)你无法在lambda中使用args...
,你可以生成一个正确的create
签名:
&T::template create<Window*&, typename std::decay<Args>::type&...>
// ~^~ ~~~~~~~~~^
最终:
static SceneBasePtr create(Window*&, const char*&, const char*&);