Во время рефакторинга некоторого кода сегодня, чтобы изменить исходные указатели на std::unique_ptr
, я столкнулся с ошибкой сегментации из-за ошибки порядка оценки.
Старый код сделал примерно следующее:
void add(const std::string& name, Foo* f)
{
_foo_map[name] = f;
}
void process(Foo* f)
{
add(f->name, f);
}
Первый, наивный, рефакторинг кода для использования std::unique_ptr
:
void add(const std::string& name, std::unique_ptr<Foo> f)
{
_foo_map[name] = std::move(f);
}
void process(std::unique_ptr<Foo> f)
{
add(f->name, std::move(f)); // segmentation-fault on f->name
}
Рефакторизованный код вызывает ошибку сегментации, потому что сначала обрабатывается второй аргумент (std::move(f)
), а затем 1-й аргумент (f->name
) разделяет перемещенную переменную, стрелу!
Возможными исправлениями являются получение дескриптора на Foo::name
, прежде чем переместить его в вызов add
:
void process(std::unique_ptr<Foo> f)
{
const std::string& name = f->name;
add(name, std::move(f));
}
Или, возможно:
void process(std::unique_ptr<Foo> f)
{
Foo* fp = f.get();
add(fp->name, std::move(f));
}
Оба этих решения требуют дополнительных строк кода и не кажутся почти такими же композитными, как и исходный (хотя и UB) вызов add
.
Вопросы:
-
Являются ли какие-либо из 2 предложенных решений выше идиоматического С++, а если нет, есть ли лучшая альтернатива?
-
Я вижу, что есть перемены, идущие на С++ 17 из-за P0145R3 - Уточнение оценки выражения для Идиоматический С++. Изменит ли это какое-либо из вышеупомянутых решений/предотвратит их подводные камни?