传递variadic arg to模板函数时的汇编误差

Compilation error when passing variadic args to a template function

本文关键字:汇编 误差 函数 variadic arg to 传递      更新时间:2023-10-16

我有以下模板方法:

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;
    });
}

本质上,我要做的就是将variadic 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中的创建来解决此问题,这在GCC 4.9及更高版本上起作用,但是我必须与GCC 4.8.4保持兼容,并且有一个错误可以防止使用lambda内的variadic args:(

update

好的,添加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]。相反,它将 decay 每个参数的类型。这意味着每个原始字符串文字将成为const char*,并将作为该类型的L值参数传递到绑定函数,并且这与手动实例化的create不匹配。window参数也是一个类似的问题。但是,它被推论为L值,但是,手动实例化的create函数期望R值。

最好不要明确指定功能模板的模板类型。如果由于某些原因(错误?),您不能在lambda内使用args...,则可以生成适当的create签名:

&T::template create<Window*&, typename std::decay<Args>::type&...>
//                        ~^~          ~~~~~~~~~^  

最终以:

static SceneBasePtr create(Window*&, const char*&, const char*&);