简体   繁体   English

std :: bind与可变参数模板函数

[英]std::bind with variadic template function

I'm trying to write a generic obj factory using variadic template to call constructors of various classes. 我正在尝试使用variadic模板编写一个泛型obj工厂来调用各种类的构造函数。 code as follow: 代码如下:

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

In most examples, variadic arg always write like this "Args&&..." in function arg list. 在大多数示例中,variadic arg总是在函数arg列表中写出这样的“Args && ...”。 but this does not work with bind, compile error msg like this (clang-902.0.39.2) 但这不适用于bind,编译错误msg就像这样(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)); 错误:没有可行的转换从'__bind(&)(int &&,int &&),int,int>'到'const ObjFactory :: CreatorFunc'(又名'const function()>')factory.registerCreator(“withArgs”, std :: bind(create_obj,1,2));

After removing the "&&", it works OK 删除“&&”后,它可以正常工作

But I don't know why? 但我不知道为什么?

Universal references only work in a deduced context. 通用引用仅适用于推断的上下文。 They do not function as you expect when the template parameters are explicitly specified. 当明确指定模板参数时,它们不会按预期运行。

Given the function template 给出功能模板

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

And the call 和电话

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

Args will be deduced to be {int&, int&} . Args将被推断为{int&, int&} Reference collapsing is applied, and int& && gets collapsed to just int& . 应用了引用折叠, int& &&折叠为int& That means the types of the values in args are {int&, int&} . 这意味着args中值的类型是{int&, int&}

If you call it with rvalues for arguments (ie foo(1, 2) ) then Args will be deduced to be {int, int} and the types of the values in args become {int&&, int&&} . 如果用参数的rvalues(即foo(1, 2) )调用它,那么Args将被推导为{int, int}args中的值的类型变为{int&&, int&&}


That's the basics of universal references, so now let's look at what happens when you call 这是通用引用的基础知识,所以现在让我们来看看你打电话时会发生什么

auto fn = std::bind(foo<int, int>, 1, 2);
fn();

Here, you've not allowed template argument deduction to take place so Args is {int, int} and foo is therefore expecting arguments of type {int&&, int&&} . 在这里,你不允许发生模板参数推断,因此Args{int, int} ,因此foo期望类型为{int&&, int&&} The values 1 , and 2 are copied into the bind object and passed to the callable as lvalues though. 12将复制到绑定对象中,并作为lvalues传递给callable。 Rvalue references can't bind to lvalues, and so the call fails to compile. Rvalue引用不能绑定到左值,因此调用无法编译。


The way to make this work correctly is to use a lambda instead of std::bind : 使其正常工作的方法是使用lambda而不是std::bind

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

With a lambda, template argument deduction works as normal, and 1 , and 2 remain rvalues. 对于lambda,模板参数推导工作正常, 12仍然是rvalues。 Everything works as expected and the universal references do their job because they're being used in a deduced context. 一切都按预期工作,通用引用完成它们的工作,因为它们被用于推断的上下文中。

when using forwarding references you need to let argument being deduced otherwise you can't perfect forward them because you won't know the real type to use for them. 当使用转发引用时,你需要让参数被推断,否则你不能完美地转发它们,因为你不会知道它们使用的真实类型。

in your case you gave a type create_obj<Derived, int, int> the function will be instantiated to std::shared_ptr<Derived> create_obj(int&&, int&&) and have no flexibility it will just be taking r-value int. 在你的情况下你给了一个类型create_obj<Derived, int, int>这个函数将被实例化为std::shared_ptr<Derived> create_obj(int&&, int&&)并且没有灵活性它只会采用r-value int。

and you assigned the callable to a const CreatorFunc& and so the closure was const and your callable couldn't receive const arguments 并且你将callable分配给const CreatorFunc&因此闭包是const而你的callable不能接收const参数

replacing create_obj<Derived, int, int> by create_obj<Derived, const int&, const int&> which cause create_obj to be instantiated as std::shared_ptr<Derived> create_obj(const int&, const int&) will work in this case but would still not have the flexibility of forwarding references. 通过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&)将在这种情况下起作用但仍然会没有转发引用的灵活性。

the real solution is to use lambda. 真正的解决方案是使用lambda。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM