Создание shared_ptr из unique_ptr
В одном из недавно рассмотренных мною фрагментов кода, который компилировался без ошибок с помощью 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 ответ(ов)
Это не должно компилироваться. Если временно оставить в стороне уникальность и совместное использование указателей, то по сути это то же самое, что и:
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 ссылке, то это будет работать (но, возможно, не так, как предполагал автор). В остальных случаях это не должно компилироваться.
Хотя это может показаться неочевидным, это вполне допустимо:
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
.
В чём разница между g++ и gcc?
Когда действительно стоит использовать noexcept?
Возможно ли вывести тип переменной в стандартном C++?
Неопределенная ссылка на виртуальную таблицу (vtable)
Почему `std::initializer_list` не поддерживает оператор подиндексации?