gpt4 book ai didi

c++ - std::bind 可变参数模板函数

转载 作者:塔克拉玛干 更新时间:2023-11-03 02:06:53 28 4
gpt4 key购买 nike

我正在尝试使用可变参数模板编写一个通用的 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;
}

在大多数示例中,可变参数总是在函数参数列表中这样写“Args&&...”。但这不适用于像这样的绑定(bind)、编译错误消息 (clang-902.0.39.2)

error: no viable conversion from '__bind (&)(int &&, int &&), int, int>' to 'const ObjFactory::CreatorFunc' (aka 'const function ()>') factory.registerCreator("withArgs", std::bind(create_obj, 1, 2));

去掉“&&”后就OK了

但我不知道为什么?

最佳答案

通用引用仅适用于推导上下文。当显式指定模板参数时,它们不会像您预期的那样运行。

给定函数模板

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

和电话

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

Args 将被推断为 {int&, int&}。引用折叠被应用,int& && 被折叠为 int&。这意味着 args 中的值的类型是 {int&, int&}

如果您使用参数的右值调用它(即 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 被复制到绑定(bind)对象中,并作为 lvalues 传递给可调用对象。右值引用无法绑定(bind)到左值,因此调用无法编译。


使它正确工作的方法是使用 lambda 而不是 std::bind:

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

对于 lambda,模板参数推导正常进行,12 仍然是右值。一切都按预期工作,通用引用发挥作用,因为它们是在推导的上下文中使用的。

关于c++ - std::bind 可变参数模板函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51791166/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com