无法在成员函数中绑定右值引用参数

Posted

技术标签:

【中文标题】无法在成员函数中绑定右值引用参数【英文标题】:Unable to bind rvalue reference arguments in member function 【发布时间】:2018-03-27 18:26:09 【问题描述】:

我正在使用 pybind11 为我无法更改其源代码的 C++ 库创建 python 绑定。它包含一个用右值引用参数定义成员函数的类(例如T &&val)。我无法使用右值引用参数创建到成员函数的绑定,但绑定到具有相同参数的非成员函数可以按预期工作。

一个简化的例子如下所示:

struct Foo 
    // Cannot create a pybinding for this method.
    void print_ref(int &&v) const 
            std::cout << "Foo::print_ref(" <<  to_string(v) << ")" <<std::endl;
    
;
// Pybinding for standalone function works as expected.
void print_ref(int&& val) 
        std::cout << "print_ref(" << to_string(val) << ")" << std::endl;
;

pybind11 代码如下:

PYBIND11_MODULE(refref, m) 
    py::class_<Foo>(m, "Foo")
    // Both of these attempts to create a pybinding FAILs with same error.
    .def("print_ref", &Foo::print_ref)
    .def("print_ref", (void (Foo::*) (int&&)) &Foo::print_ref);
    // This pybinding of standalone function is SUCCESSful.
    m.def("print_ref", &print_ref);

第一次绑定尝试的编译错误是:

pybind11/bin/../include/site/python3.4/pybind11/pybind11.h:79:80: error: rvalue reference to type 'int' cannot bind to lvalue of type 'int'
        initialize([f](const Class *c, Arg... args) -> Return  return (c->*f)(args...); ,
                                                                               ^~~~
pybind11/bin/../include/site/python3.4/pybind11/pybind11.h:1085:22: note: in instantiation of function template specialization 'pybind11::cpp_function::cpp_function<void, Foo, int &&,
      pybind11::name, pybind11::is_method, pybind11::sibling>' requested here
        cpp_function cf(method_adaptor<type>(std::forward<Func>(f)), name(name_), is_method(*this),
                     ^
refref.cpp:31:3: note: in instantiation of function template specialization 'pybind11::class_<Foo>::def<void (Foo::*)(int &&) const>' requested here
        .def("print_ref", &Foo::print_ref);

关于我可能做错的任何想法?由于它适用于非成员函数,我倾向于怀疑 pybind11 问题,但我想我会先在这里检查。

【问题讨论】:

【参考方案1】:

确实,问题来自rvalues。我从this SO answer 和this blog post 学到了很多东西。 有一个很好的解决方法:您可以创建一个包装类,它将调用重定向到您无法更改的 C++ 库,并使用 std::move 语义处理 rvalue

#include <iostream>
#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>

namespace py = pybind11;

struct Foo 
    void print_ref(int&& v) const 
            std::cout << "Foo::print_ref("  <<  +v << ")" <<std::endl;
    
;


// This class will interface between PyBind and your library
class Foo_wrap
private:
  Foo _mimu;
public:
  void print_ref(int v) const
    _mimu.print_ref(std::move(v));
  
;



PYBIND11_MODULE(example, m) 
     py::class_<Foo_wrap>(m, "Foo_wrap")
     .def(py::init())
     .def("print_ref", &Foo_wrap::print_ref);

你可以用 Python 调用

import example as fo

wr = fo.Foo_wrap()
wr.print_ref(2)

【讨论】:

以上是关于无法在成员函数中绑定右值引用参数的主要内容,如果未能解决你的问题,请参考以下文章

右值引用成员变量有啥用

重新理解C11的右值引用

错误:对类型为'const ItemInstance'的引用无法绑定到类型为'void'的右值

将临时右值绑定到 std::vector 构造函数中的引用左值

右值引用的参数传递疑问(C++0x)

error C2662 无法将左值绑定到右值 —— 变量永远是左值,即使它的类型为右值引用