【发布时间】:2016-10-10 02:49:34
【问题描述】:
我有一些现有的 C++98 代码,它们使用 boost::function 和 boost: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,我有三种选择:
声明多个重载(一个带有
const&,一个带有&&)——但由于它有两个参数,这两个参数都可以从移动语义中受益,这将需要四个重载——呈指数级恶化对于具有更多参数的方法。此外,这会导致代码重复或将代码分散到其他方法上,从而影响可读性。按值传递和移动(类似于
WriteOperation构造函数)。当主体总是进行复制时,这可能是最简洁的选择,如果实际调用了WriteOperation构造函数,则为 true,但是如果省略的部分包含可能在不构造WriteOperation的情况下返回的逻辑怎么办?在这种情况下有一个浪费的副本。模板和完善转发。这需要一个丑陋的 SFINAE hack,它会混淆 Intellisense 并损害可读性(或者更糟糕的是,使参数类型不受约束),并且需要将实现放入标头中,这有时是不可取的。 而且它会干扰类型转换,例如。寻找
std::string的 SFINAEenable_ifis_same不会接受const char *文字,而原始的const&版本会。
我错过了什么吗?有更好的解决方案吗?还是这只是移动语义没有任何区别的情况?
一个相关案例:
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);
}
这一切看起来应该没问题,没有复制也不需要移动语义。我再次不确定 ReadHandler 是否传递给 Read。
【问题讨论】:
-
“因为这有两个参数,这两个参数都可以从移动语义中受益”,是那些?在您的示例代码中,您将所有三个参数都视为受益于移动语义。
-
Device::Write只有两个参数。WriteOperation构造函数有第三个参数,但由于这是通过shared_from_this()传递的,所以无论如何它都会被复制。 -
嗯,
Device::Write被调用的可能性有多大,将临时变量作为实际参数,优化这种情况有多重要? -
如果你跟踪调用链足够远,它会在一个局部变量处结束(在调用之后不再使用),所以可能总是这样,假设那些中间层也移动了。至于它有多重要,这是我想弄清楚的。
标签: c++ c++11 move-semantics boost-bind boost-function