Возвращает unique_ptr <Object>как unique_ptr <const Object> - PullRequest
0 голосов
/ 05 мая 2018

У меня есть такой метод:

std::unique_ptr<const Stats> Table::GetStats() const {
  std::unique_ptr<Stats> result;
  // ...
  // Prepare stats. Under some conditions an exception may be thrown.
  // ...
  return result;
}

Проблема в том, что он не компилируется:

ошибка: невозможно связать значение ‘std :: unique_ptr ’с std :: unique_ptr &&’

Я могу сделать это с помощью следующего обхода:

return std::unique_ptr<const Stats>(result.release());

Но это кажется чем-то чрезмерным. Я не могу понять, что не так с первым фрагментом кода с точки зрения C ++? Есть ли более элегантное решение?

1 Ответ

0 голосов
/ 05 мая 2018

Ваш код должен работать нормально, потому что в return return :

(акцент мой)

(начиная с C ++ 11)

Если выражение является выражением lvalue и условиями для копирования соблюдаются или будут соблюдены, за исключением того, что параметр функции, затем разрешение перегрузки, чтобы выбрать конструктор Для использования для инициализации возвращаемое значение выполняется дважды: сначала, как если бы выражение было rvalue выражением (таким образом, оно может выбрать конструктор перемещения или конструктор копирования со ссылкой на const), и если нет подходящего преобразования, разрешение перегрузки выполняется во второй раз, с выражением lvalue (так что он может выбрать конструктор копирования, ссылающийся на неконстантный).

Приведенное выше правило применяется, даже если тип возвращаемого значения функции отличается из типа выражения (для копирования требуется тот же тип)

Это означает, что даже result является lvalue, сначала оно будет рассматриваться как r-значение, затем будет выбран следующий конструктор , который может преобразовать std::unique_ptr<Stats> в std::unique_ptr<const Stats>.

template< class U, class E >
unique_ptr( unique_ptr<U, E>&& u ) noexcept;

Кажется, что gcc4.9.2 не ведет себя таким образом (т.е. сначала обрабатывает выражение как выражение rvalue); gcc 9 просто отлично работает.

Как прокомментировал @ RichardHodges , вы можете использовать std::move в качестве обходного пути .

...