c++ - 将 C++11 move 语义应用于绑定(bind)函数

标签 c++ c++11 move-semantics boost-bind boost-function

我有一些现有的 C++98 代码,它们使用 boost::functionboost:bind 进行异步回调。一些相关的简化代码片段包括:

typedef boost::function<void (boost::system::error_code, size_t)> WriteHandler;

struct WriteOperation
{
    WriteOperation(const boost::shared_ptr<IDevice>& device,
                   const std::string& data, const WriteHandler& handler)
        : m_Device(device), m_Data(data), m_Handler(handler) {}

private:
    boost::shared_ptr<IDevice> m_Device;
    std::string m_Data;
    WriteHandler m_Handler;

    void Complete()
    {
        boost::system::error_code ec;
        size_t len;
        ...
        Async::Post(boost::bind(m_Handler, ec, len));
    }
};

struct Device : public IDevice
{
    void Write(const std::string& data, const WriteHandler& callback)
    {
        ...
        Async::Start(new WriteOperation(shared_from_this(), data,
            boost::bind(&Device::HandleWrite, this, handler, _1, _2)));
    }

 private:
    void HandleWrite(const WriteHandler& callback,
                     boost::system::error_code ec, size_t len)
    {
        ...
        callback(ec, len);
    }
};

在构造 WriteOperation 时需要一个拷贝,但除此之外我尽量避免拷贝,因为它们可能非常昂贵。

我正在考虑如何在 C++11 世界中最好地编写它。显而易见的容易实现的成果是 WriteOperation 构造函数在内部将其参数复制到其字段,因此应该使用自动复制习惯用法:

WriteOperation(boost::shared_ptr<IDevice> device,
               std::string data, WriteHandler handler)
    : m_Device(std::move(device)), m_Data(std::move(data)), m_Handler(std::move(handler))
{}

(当然,裸露的 new 应该替换为 unique_ptr,但这是一个附带问题。)

但是,鉴于当前 Device::Write 的实现,我不认为这实际上有任何好处,所以这也应该改变。我的问题是我真的没有看到一个好的方法来做到这一点。根据this advice ,我有三个选择:

  1. 声明多个重载(一个使用 const&,一个使用 &&)——但是因为它有两个参数,这两个参数都可以从 move 语义中获益,这将需要四次重载——对于具有更多参数的方法,情况会呈指数级恶化。此外,这会导致代码重复或将代码散布在其他方法上,从而影响可读性。

  2. 按值传递并 move (类似于WriteOperation 构造函数)。这可能是当正文总是制作拷贝时最干净的选项,如果实际调用了 WriteOperation 构造函数,这是正确的,但是如果省略的部分包含可能返回而不构造 WriteOperation 的逻辑怎么办?在这种情况下有一个浪费的拷贝。

  3. 模板和完美转发。这需要一个丑陋的 SFINAE hack,它会混淆 Intellisense 并削弱可读性(或者更糟,使参数类型不受约束),并且需要将实现放入 header 中,这有时是不可取的。 并且它会干扰类型转换,例如。寻找 std::string 的 SFINAE enable_if is_same 将不接受 const char * 文字,而原始的const& 版本会。

我错过了什么吗?有更好的解决方案吗?或者这只是 move 语义没有任何区别的情况?


相关案例:

typedef boost::function<void (boost::system::error_code, const std::string&)> ReadHandler;

void Read(const ReadHandler& callback)
{
    ... boost::bind(&Device::HandleRead, this, callback, _1, _2) ...
}

void HandleRead(const ReadHandler& callback,
                boost::system::error_code ec, const std::string& data)
{
    ...
    callback(ec, data);
}

这一切看起来应该没问题,没有复制也不需要 move 语义。我再次不确定传递给 ReadReadHandler

最佳答案

大致顺序:

如果复制和 move 一样昂贵,请用 const& .

如果您可靠地保留一份拷贝,并且搬家很便宜,请按值(value)获取。

如果做不到这一点,您可以将其填充到标题中,并且可以使用 sfinae 或不受约束的模板,使用转发引用。

否则,如果参数数量有限,请编写每个重载。这是 2^n在参数的数量上,所以最好不要太多。在内部转发到基于转发引用的实现。

否则,您真的需要效率吗?

否则,输入 erase 到“T 的创建者”。

template<class T, using Base=std::function<T()>>
struct creator_of: Base
{
  template<class U,
    std::enable_if_t<std::is_constructible<T, U&&>{},int> =0
  >
  creator_of(U&&u):
    Base([&]()->T{ return std::forward<U>(u); })
  {}
  template<class U,
    std::enable_if_t<std::is_constructible<T, std::result_of_t<std::decay_t<U>()>{},int> =0
  >
  creator_of(U&&u):
    Base(std::forward<U>(u))
  {}

  creator_of(creator_of&&)=default;
  creator_of():
    Base([]()->T{return {};}}
  {}
};

根据需要增加。 creator_of<std::string>可以由可以构建 std::string 的事物构建,或来自返回 std::string 的函数对象.

内部您可以调用()一次就可以了。

(代码未编译,但设计合理。)

关于c++ - 将 C++11 move 语义应用于绑定(bind)函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39950667/

相关文章:

c++11 - 基于 C++ 中可能输入的最大范围推导返回类型

c++ - 像 C::C(const C&&) 这样的构造函数我们怎么调用它?除了禁止从 const 移动外,还有其他用途吗?

rust - 有朝一日,Rust 可以在对象 move 期间优化掉按位复制吗?

c++ - move 语义、奇怪的重复模板模式和内存泄漏

c++ - 对象在 push_back 进入 vector 后立即销毁

c++ - 为什么这里调用复制构造函数,而不是 move 构造函数?

c++ - Python ImportError - undefined symbol - 用于自定义 C++ 模块

c++ - 使用 boost::geometry::append 时,自定义点类中的 ID 字段间歇性丢失

c++ - 使用快速傅里叶变换模糊矩阵

c++ - 在 C++ 中用字符串索引的整数