У меня есть функция, которая работает с большим количеством данных, передаваемых в качестве аргумента приемника. мой BigData
type уже поддерживает C ++ 11 и поставляется с полностью функциональным конструктором перемещения и реализациями перемещения, так что я могу уйти, не копируя эту чертову вещь:
Result processBigData(BigData);
[...]
BigData b = retrieveData();
Result r = processBigData(std::move(b));
Это все прекрасно работает. Тем не менее, моя функция обработки может иногда не работать во время выполнения, что приводит к исключению. Это на самом деле не проблема, так как я могу просто исправить вещи и повторить попытку:
BigData b = retrieveData();
Result r;
try {
r = processBigData(std::move(b));
} catch(std::runtime_error&) {
r = fixEnvironmnentAndTryAgain(b);
// wait, something isn't right here...
}
Конечно, это не сработает.
Так как я переехал мои данные в функцию обработки, когда я прибуду в обработчик исключений, b
больше не будет использоваться
Это грозит радикально уменьшить мой энтузиазм по поводу передачи аргументов в пользу стоимости.
Итак, вот вопрос: как справиться с такой ситуацией в современном коде C ++? Как получить доступ к данным, которые ранее были перемещены в функцию, которая не была выполнена?
Вы можете изменить реализацию и интерфейсы для обоих BigData
а также processBigData
как пожелаете. Однако в конечном решении следует попытаться свести к минимуму недостатки исходного кода, касающиеся эффективности и удобства использования.
Я так же в замешательстве от этой проблемы.
Насколько я могу судить, лучшая текущая идиома — разделить передачу по значению на пару ссылок по ссылкам.
template< typename t >
std::decay_t< t >
val( t && o ) // Given an object, return a new object "val"ue by move or copy
{ return std::forward< t >( o ); }
Result processBigData(BigData && in_rref) {
// implementation
}
Result processBigData(BigData const & in_cref ) {
return processBigData( val( in_cref ) );
}
Конечно, кусочки аргумента могли быть перемещены до исключения. Проблема распространяется на что угодно processBigData
звонки.
У меня появилось вдохновение для разработки объекта, который возвращается к своему источнику при определенных исключениях, но это решение конкретной проблемы на горизонте в одном из моих проектов. Это может оказаться слишком специализированным или вообще неосуществимым.
По всей видимости, этот вопрос активно обсуждался на недавней конференции CppCon 2014. Херб Саттер в своем заключительном выступлении подытожил последние события: Вернуться к истокам! Основы современного стиля C ++ (слайды).
Его вывод довольно прост: Не используйте передачу по значению для аргументов приемника.
Аргументы для использования этой техники в первую очередь (как популяризировано Эриком Найблером Meeting C ++ 2013 keynote C ++ 11 Дизайн библиотеки (слайды)Кажется, перевешиваются недостатки. Первоначальной мотивацией для передачи аргументов-приемников по значению было избавление от комбинаторного взрыва для перегрузок функций, возникающих в результате использования const&
/&&
,
К сожалению, похоже, что это приводит к ряду непреднамеренных последствий. Одним из них являются потенциальные недостатки эффективности (в основном из-за ненужных буферных распределений). Другая проблема заключается в исключении безопасности из этого вопроса. Оба из них обсуждаются в разговоре Херб.
Херб вывод не использовать передаваемый по значению аргумент приемника, но вместо этого полагаться на отдельные const&
/&&
(с const&
быть по умолчанию и &&
зарезервировано для тех немногих случаев, когда требуется оптимизация).
Это также соответствует тому, что @ Potatoswatter ответ предложил. Передав аргумент раковины через &&
мы могли бы отложить фактическое перемещение данных от аргумента до точки, где мы можем дать гарантию без исключения.
Мне в некотором роде понравилась идея передачи аргументов по значению, но кажется, что на практике это не так хорошо, как все надеялись.