0

Создание shared_ptr из unique_ptr

1

В одном из недавно рассмотренных мною фрагментов кода, который компилировался без ошибок с помощью g++-4.6, я столкнулся с странной попыткой создать std::shared_ptr из std::unique_ptr:

std::unique_ptr<Foo> foo...
std::make_shared<Foo>(std::move(foo));

Это кажется мне довольно странным. По всей видимости, правильнее было бы записать это как std::shared_ptr<Foo>(std::move(foo));, насколько я понимаю, хотя я не совсем уверенно разбираюсь в перемещениях (и знаю, что std::move является лишь приведением типа, ничего не перемещая).

Я проверил это на разных компиляторах и получил следующие результаты:

#include <memory>

int main()
{
   std::unique_ptr<int> foo(new int);
   std::make_shared<int>(std::move(foo));
}

Результаты компиляции:

  • g++-4.4.7 - выдает ошибку компиляции
  • g++-4.6.4 - компилируется без ошибок
  • g++-4.7.3 - выдает внутреннюю ошибку компилятора
  • g++-4.8.1 - выдает ошибку компиляции
  • clang++-3.2.1 - компилируется без ошибок

Итак, вопрос в следующем: какой компилятор прав с точки зрения стандарта? Стандарт требует, чтобы это было недопустимое выражение, допустимым выражением или это просто неопределенное поведение?

Дополнение:

Мы пришли к выводу, что некоторые из этих компиляторов, такие как clang++ и g-4.6.4, допускают преобразование, хотя не должны. Однако g-4.7.3 (который выдает внутреннюю ошибку компилятора при std::make_shared<Foo>(std::move(foo));) корректно отклоняет int bar(std::move(foo));.

Из-за этого огромного различия в поведении я оставляю вопрос в его текущем виде, хотя часть из него можно было бы разрешить с помощью упрощения до int bar(std::move(foo));.

*) NUC: Не универсально компилируемый

2 ответ(ов)

0

Это не должно компилироваться. Если временно оставить в стороне уникальность и совместное использование указателей, то по сути это то же самое, что и:

int *u = new int;
int *s = new int(std::move(u));

Это означает, что происходит динамическое создание int и инициализация его rvalue ссылкой на std::unique_ptr<int>. В случае с int это просто не должно компилироваться.

Что касается общего класса Foo, то все зависит от определения этого класса. Если у него есть конструктор, принимающий std::unique_ptr<Foo> по значению, const ссылке или rvalue ссылке, то это будет работать (но, возможно, не так, как предполагал автор). В остальных случаях это не должно компилироваться.

0

Хотя это может показаться неочевидным, это вполне допустимо:

std::shared_ptr<Type> shp = std::make_unique<Type>(arg);

На самом деле, std::make_unique создает объект типа Type и возвращает std::unique_ptr<Type>, а затем вы можете преобразовать этот указатель в std::shared_ptr<Type>. Однако стоит заметить, что это не совсем корректный код, так как std::make_unique возвращает std::unique_ptr, а не shared_ptr.

Если вы хотите создать shared_ptr прямо, вы можете использовать std::make_shared:

std::shared_ptr<Type> shp = std::make_shared<Type>(arg);

Это создаст объект Type и вернет его в виде std::shared_ptr, что более эффективно и предпочтительнее по сравнению с преобразованием unique_ptr в shared_ptr.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь