C++ 将具有唯一_ptr参数的函数绑定到std::function<;void()>;

C++ 将具有唯一_ptr参数的函数绑定到std::function<;void()>;,c++,c++11,C++,C++11,我正在努力使以下代码正常工作: #include <cstdio> #include <functional> #include <string> #include <memory> using namespace std; class Foo { public: Foo(): m_str("foo") { } void f1(string s1, string s2, unique_ptr<Foo> p)

我正在努力使以下代码正常工作:

#include <cstdio>
#include <functional>
#include <string>
#include <memory>

using namespace std;

class Foo {
public:
    Foo(): m_str("foo") { }

    void f1(string s1, string s2, unique_ptr<Foo> p)
        {
            printf("1: %s %s %s\n", s1.c_str(), s2.c_str(), p->str());
        }

    void f2(string s1, string s2, Foo* p)
        {
            printf("2: %s %s %s\n", s1.c_str(), s2.c_str(), p->str());
        }

    const char* str() const { return m_str.c_str(); }

private:
    string m_str;
};

int main()
{
    string arg1 = "arg1";
    string arg2 = "arg2";
    Foo s;
    unique_ptr<Foo> ptr(new Foo);


    //function<void()> f(bind(&Foo::f1, &s, arg1, arg2, std::move(ptr)));
    function<void()> f(bind(&Foo::f2, &s, arg1, arg2, ptr.release()));

    f();
}
我做错了什么


我使用的是GCC4.8.2和
-std=c++0x
-std=c++11
也失败)标志。

Hmm似乎std::bind在处理r值引用时遇到了问题。一种替代方法是使用lambda函数:

function<void()> f([&]() { s.f1(arg1,arg2,std::move(ptr)); });
函数f([&](){s.f1(arg1,arg2,std::move(ptr));}); 为了实现这一点,您还必须更改f1的签名,使其接受唯一的\u ptr作为r值参考:

void f1(string s1, string s2, unique_ptr<Foo>&& p)
void f1(字符串s1、字符串s2、唯一\u ptr&p)
(即使std::bind可以处理r值引用,您仍然必须这样做,因为std::unique\u ptr没有复制构造函数,只有移动构造函数是可访问的!)


但是请注意,您的构造相当危险(如果std::bind可以工作的话):如果您调用f()两次,您将得到一个运行时异常。

其他答案中描述的bind问题(在撰写本文时)并不是编译器在问题中抱怨的问题。问题是
std::function
必须是可复制的,这要求它的参数(将由函数存储)也是可复制的

来自标准[20.9.11.2类模板函数]

template<class F> function(F f); 
template <class F, class A> function(allocator_arg_t, const A& a, F f);
模板函数(F);
模板函数(分配器参数、常数A&A、F);
要求:F应可复制。对于参数类型ArgTypes和返回类型R,f应是可调用的(20.9.11.2)。的复制构造函数和析构函数不应引发异常

考虑这个例子,它甚至不包括bind:

#include <functional>
#include <memory>

using namespace std;

struct NonCopyableFunctor {
  NonCopyableFunctor(){}
  NonCopyableFunctor(const NonCopyableFunctor &) = delete;
  NonCopyableFunctor(NonCopyableFunctor &&){}
  void operator()(){}
};

int main() 
{
  NonCopyableFunctor fun;
  function<void()> vfun(move(fun)); // even though I move here,
  // it still complains about a copy going on elsewhere.
}
#包括
#包括
使用名称空间std;
结构非copyablefunctor{
非copyablefunctor(){}
NonCopyableFunctor(const NonCopyableFunctor&)=delete;
非copyablefunctor(非copyablefunctor&&{}
void运算符()({}
};
int main()
{
非copyablefunctor-fun;
函数vfun(move(fun));//即使我搬到这里,
//它仍然抱怨在其他地方复制。
}
以下是clang的输出:

[orm@localhost ~]$ clang++ -std=c++11 bound_unique.cc 
In file included from bound_unique.cc:1:
/usr/bin/../lib/gcc/x86_64-redhat-linux/4.8.2/../../../../include/c++/4.8.2/functional:1911:10: error: call to deleted constructor of 'NonCopyableFunctor'
            new _Functor(*__source._M_access<_Functor*>());
                ^        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/bin/../lib/gcc/x86_64-redhat-linux/4.8.2/../../../../include/c++/4.8.2/functional:1946:8: note: in instantiation of member function
      'std::_Function_base::_Base_manager<NonCopyableFunctor>::_M_clone' requested here
              _M_clone(__dest, __source, _Local_storage());
              ^
/usr/bin/../lib/gcc/x86_64-redhat-linux/4.8.2/../../../../include/c++/4.8.2/functional:2453:33: note: in instantiation of member function
      'std::_Function_base::_Base_manager<NonCopyableFunctor>::_M_manager' requested here
            _M_manager = &_My_handler::_M_manager;
                                       ^
bound_unique.cc:16:20: note: in instantiation of function template specialization 'std::function<void ()>::function<NonCopyableFunctor, void>' requested here
  function<void()> vfun(move(fun));
                   ^
bound_unique.cc:8:3: note: function has been explicitly marked deleted here
  NonCopyableFunctor(const NonCopyableFunctor &) = delete;
  ^
1 error generated.
[orm@localhost~]$clang++-std=c++11绑定_unique.cc
在绑定_unique.cc:1中包含的文件中:
/usr/bin/./lib/gcc/x86_64-redhat-linux/4.8.2/../../../../../../../include/c++/4.8.2/functional:1911:10:错误:调用已删除的“NonCopyableFunctor”构造函数
新的_函子(*__source._M_access());
^        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/bin/./lib/gcc/x86_64-redhat-linux/4.8.2/../../../../../../../include/c++/4.8.2/functional:1946:8:注意:在成员函数的实例化中
“std::_函数_库::_库_管理器::_M_克隆”已在此处请求
_M_clone(u dest,u source,_Local_storage());
^
/usr/bin/./lib/gcc/x86_64-redhat-linux/4.8.2/../../../../../../../include/c++/4.8.2/functional:2453:33:注意:在成员函数的实例化中
“std::_功能_基础::_基础_管理者::_管理者”已在此处请求
_M_经理=&_我的处理程序::_M_经理;
^
绑定_unique.cc:16:20:注意:在函数模板专门化的实例化中,此处请求“std::function::function”
功能vfun(移动(乐趣));
^
绑定_unique.cc:8:3:注意:函数已在此处显式标记为删除
NonCopyableFunctor(const NonCopyableFunctor&)=delete;
^
生成1个错误。
请注意,如果绑定唯一的\u ptr,则生成的绑定对象将不可复制。Bind仍将编译。

1)以下代码不会编译

 function<void()> f(bind(&Foo::f1, &s, arg1, arg2, std::move(ptr)));
 // just define f, not even call it
因为在步骤(a)
bind
将您给予它的内容存储为左值(除了
reference\u wrapper
),并在步骤(b)将其传递给内部函子。因此,它要求绑定参数是可复制的,因为这些参数是通过值传递的,而不是通过引用传递的

3) 然后尝试使用原始指针。但是,下面的代码也不会编译

 auto f(bind(&Foo::f1, &s, arg1, arg2, std::move(ptr))); // a
 f();                                                    // b
 auto f(bind(&Foo::f1, &s, arg1, arg2, ptr.release()));
 f();
类似于(2)的原因,functor存储一个
int*
,并在调用时尝试将其转换为参数类型
unique\u ptr
。但是构造函数
unique\u ptr(指针p)
显式的


要编译它,您需要这样一个函数

void f3(string s1, string s2, unique_ptr<Foo>& p)
//                                           ^ use reference; add const if you need
{
    printf("3: %s %s %s\n", s1.c_str(), s2.c_str(), p->str());
}

auto f(bind(&Foo::f3, &s, arg1, arg2, std::move(ptr)));
f();
void f3(字符串s1、字符串s2、唯一\u ptr&p)
//^使用参考资料;如果需要,请添加常量
{
printf(“3:%s%s%s\n”,s1.c_str(),s2.c_str(),p->str());
}
自动f(绑定(&Foo::f3,&s,arg1,arg2,std::move(ptr));
f();

请注意,可以多次调用
f
,并且参数
p
引用了存储在
bind
的返回对象中的相同
unique\ptr

将右值传递给std::bind有问题,请参见f1是否确实需要获取指针的所有权?f1可以通过const ref获取指针,然后您可以使用std::ref将指针传递到std::bind。请参见@justinls-Yup,它需要获得所有权,但感谢您的示例。如果我没有找到使用std::move使其工作的方法,我将使用您的建议提出一个解决方案。如果您的函数没有将r值引用到您的唯一指针(std::unique_ptr&&),那么为什么要使用std::move?请注意,在C++11中,您无法轻松地在这样的lambda表达式中捕获
unique_ptr
。如果您尝试这样做,您将在运行时得到一个空指针异常,如果您在原始
unique\u ptr
超出范围后调用该函数。请参阅以了解一些解决方法。
void f3(string s1, string s2, unique_ptr<Foo>& p)
//                                           ^ use reference; add const if you need
{
    printf("3: %s %s %s\n", s1.c_str(), s2.c_str(), p->str());
}

auto f(bind(&Foo::f3, &s, arg1, arg2, std::move(ptr)));
f();