我有一些现有的 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&
,一个使用&&
)——但是因为它有两个参数,这两个参数都可以从 move 语义中获益,这将需要四次重载——对于具有更多参数的方法,情况会呈指数级恶化。此外,这会导致代码重复或将代码散布在其他方法上,从而影响可读性。按值传递并 move (类似于
WriteOperation
构造函数)。这可能是当正文总是制作拷贝时最干净的选项,如果实际调用了WriteOperation
构造函数,这是正确的,但是如果省略的部分包含可能返回而不构造WriteOperation 的逻辑怎么办
?在这种情况下有一个浪费的拷贝。模板和完美转发。这需要一个丑陋的 SFINAE hack,它会混淆 Intellisense 并削弱可读性(或者更糟,使参数类型不受约束),并且需要将实现放入 header 中,这有时是不可取的。 并且它会干扰类型转换,例如。寻找
std::string
的 SFINAEenable_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 语义。我再次不确定传递给 Read
的 ReadHandler
。
最佳答案
大致顺序:
如果复制和 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/