将可变参数传递给模板函数时出现编译错误

我有以下模板方法:

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 :()内使用可变参数的错误:

UPDATE

OK,因此添加std :: decay(如注释中所示)并未完全解决问题,第一个参数一直推演到Window*&&而不是Window* ,但实际上指定了func的类型(例如std::function<SceneBasePtr (Window*)> )而不是使用auto编译的东西。

我不确定这是为什么......


您没有显示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*&);
链接地址: http://www.djcxy.com/p/51585.html

上一篇: Compilation error when passing variadic args to a template function

下一篇: Virtual method with variadic arguments