11

From cppreference:

In C++11 and C++14 it is valid to construct a std::shared_ptr<T> from a std::unique_ptr<T[]>:

std::unique_ptr<int[]> arr(new int[1]);
std::shared_ptr<int> ptr(std::move(arr));

Since the shared_ptr obtains its deleter (a std::default_delete<T[]> object) from the unique_ptr, the array will be correctly deallocated.

This is no longer allowed in C++17. Instead the array form std::shared_ptr<T[]> should be used.

Why is it not allowed in C++17? What has changed?

kennytm
  • 510,854
  • 105
  • 1,084
  • 1,005
ikh
  • 10,119
  • 1
  • 31
  • 70
  • Some readings if you want: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0414r2.html – Holt Apr 09 '17 at 09:44

1 Answers1

14

p0497r0:

Incorrect constraint for shared_ptr construction from unique_ptr

[...]

Based on implementation experience I believe the correct form is:

        Remark: This constructor shall not participate in overload resolution unless Y* is compatible with T* and unique_ptr<Y, D>::pointer is convertible to element_type*.

The "compatible with" check prevents undesirable conversions from unique_ptr<T[]> to shared_ptr<T> and the "convertible to" check ensures that the result of unique_ptr<Y, D>::get() can be stored in the shared_ptr and returned by shared_ptr<T>::get().

In other words, this was intentionally made invalid just because it shouldn't be valid, not simply a side effect of other changes.

Which makes sense to me. shared_ptr<T> is likely to be read by other programmers as pointing to only one T object. Requiring programmers to use shared_ptr<T[]> when they want multiple T objects leads to more readable code.

Note: this correct form is not in the standard. The rationale, however, is in part a comment on what is in the standard.

Community
  • 1
  • 1
  • I think it is reasonable that `unique_ptr` should not be converted into `shared_ptr`. But is it worth breaking backward-compatibility? – ikh Apr 09 '17 at 09:56
  • 3
    @ikh Keep in mind that originally, there were *no* checks at all, this meant that generic code couldn't use SFINAE to check that implicitly converting `std::unique_ptr` to `std::shared_ptr` is nonsense. (`static_assert(std::is_convertible, std::shared_ptr>(), "");` was required to pass.) If the wording is adjusted to correct this, yes, I think it's worth getting it right straight away. –  Apr 09 '17 at 10:06
  • @ikh also the shared ptr aliasing constructor is a workaround if we need that particular conversion. Like `auto tmp=shared_ptr( std::move(up) ); auto sp=shared_ptr( tmp.get(), tmp );` – Yakk - Adam Nevraumont Apr 09 '17 at 11:16