У меня есть некоторый код C++ 98, который использует для асинхронных обратных вызовов boost::function
и boost:bind
. Некоторые соответствующие упрощенные фрагменты кода включают в себя:Применение семантики перемещения C++ 11 к связанным функциям
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
, но что, если разрешенный раздел содержит логику, которая может вернуться без построенияWriteOperation
? В этом случае есть потерянная копия.Шаблон и идеальный вперед. Это требует уродливого хакера SFINAE, который смущает Intellisense и ухудшает читаемость (или, что еще хуже, оставляя тип параметра безусловным) и требует, чтобы реализация была помещена в заголовок, что иногда нежелательно. И он мешает преобразованию типов, например. a SFINAE
enable_if
is_same
ищетstd::string
не принимается буквами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() ', он всегда будет скопирован в любом случае. – Miral
Хорошо, насколько вероятно, что 'Device :: Write' будет вызываться с временными в качестве фактических аргументов и насколько важно оптимизировать этот случай? –