2

Is there any reason why the following should not work in c++11 (or later)?

#include <memory>

int main()
{
    auto up = std::unique_ptr<int[]>(new int[5]{});
    auto sp = std::shared_ptr<int>(std::move(up));
}

My expectation was that this would use the 13th constructor listed here at cppreference.com.

When checking at https://gcc.godbolt.org/, visual studio and gcc compile this without a problem, but clang++ - or more specifically libc++ (-stdlib=libc++) - throws an elaborate error:

<source>:6:13: error: no matching conversion for functional-style cast from 'typename remove_reference<unique_ptr<int const[], default_delete<int const[]> > &>::type' (aka 'std::__1::unique_ptr<int const[], std::__1::default_delete<int const[]> >') to 'std::shared_ptr<const int>'
auto sp = std::shared_ptr<const int>(std::move(up));
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[..]c++/v1/memory:3900:23: note: candidate constructor not viable: no known conversion from 'typename remove_reference<unique_ptr<int const[], default_delete<int const[]> > &>::type' (aka 'std::__1::unique_ptr<int const[], std::__1::default_delete<int const[]> >') to 'nullptr_t' for 1st argument
_LIBCPP_CONSTEXPR shared_ptr(nullptr_t) _NOEXCEPT;
^
[..]c++/v1/memory:3914:5: note: candidate constructor not viable: no known conversion from 'typename remove_reference<unique_ptr<int const[], default_delete<int const[]> > &>::type' (aka 'std::__1::unique_ptr<int const[], std::__1::default_delete<int const[]> >') to 'const std::__1::shared_ptr<const int>' for 1st argument
shared_ptr(const shared_ptr& __r) _NOEXCEPT;
^
[..]c++/v1/memory:3922:5: note: candidate constructor not viable: no known conversion from 'typename remove_reference<unique_ptr<int const[], default_delete<int const[]> > &>::type' (aka 'std::__1::unique_ptr<int const[], std::__1::default_delete<int const[]> >') to 'std::__1::shared_ptr<const int>' for 1st argument
shared_ptr(shared_ptr&& __r) _NOEXCEPT;
^
[..]c++/v1/memory:3902:18: note: candidate template ignored: could not match '_Yp *' against 'typename remove_reference<unique_ptr<int const[], default_delete<int const[]> > &>::type' (aka 'std::__1::unique_ptr<int const[], std::__1::default_delete<int const[]> >')
explicit shared_ptr(_Yp* __p,
^
[..]c++/v1/memory:3917:9: note: candidate template ignored: could not match 'shared_ptr' against 'unique_ptr'
shared_ptr(const shared_ptr<_Yp>& __r,
^
[..]c++/v1/memory:3923:52: note: candidate template ignored: could not match 'shared_ptr' against 'unique_ptr'
template<class _Yp> _LIBCPP_INLINE_VISIBILITY  shared_ptr(shared_ptr<_Yp>&& __r,
^
[..]c++/v1/memory:3927:34: note: candidate template ignored: could not match 'weak_ptr' against 'unique_ptr'
template<class _Yp> explicit shared_ptr(const weak_ptr<_Yp>& __r,
^
[..]c++/v1/memory:3931:9: note: candidate template ignored: could not match 'auto_ptr' against 'unique_ptr'
shared_ptr(auto_ptr<_Yp>&& __r,
^
[..]c++/v1/memory:3943:24: note: candidate template ignored: disabled by 'enable_if' [with _Yp = int const[], _Dp = std::__1::default_delete<int const[]>]
!is_lvalue_reference<_Dp>::value &&
^
[..]c++/v1/memory:3952:24: note: candidate template ignored: disabled by 'enable_if' [with _Yp = int const[], _Dp = std::__1::default_delete<int const[]>]
is_lvalue_reference<_Dp>::value &&
^
[..]c++/v1/memory:3905:9: note: candidate constructor template not viable: requires at least 2 arguments, but 1 was provided
shared_ptr(_Yp* __p, _Dp __d,
^
[..]c++/v1/memory:3908:9: note: candidate constructor template not viable: requires at least 3 arguments, but 1 was provided
shared_ptr(_Yp* __p, _Dp __d, _Alloc __a,
^
[..]c++/v1/memory:3910:26: note: candidate constructor template not viable: requires 2 arguments, but 1 was provided
template <class _Dp> shared_ptr(nullptr_t __p, _Dp __d);
^
[..]c++/v1/memory:3911:40: note: candidate constructor template not viable: requires 3 arguments, but 1 was provided
template <class _Dp, class _Alloc> shared_ptr(nullptr_t __p, _Dp __d, _Alloc __a);
^
[..]c++/v1/memory:3912:51: note: candidate constructor template not viable: requires 2 arguments, but 1 was provided
template<class _Yp> _LIBCPP_INLINE_VISIBILITY shared_ptr(const shared_ptr<_Yp>& __r, element_type* __p) _NOEXCEPT;
^
[..]c++/v1/memory:3898:23: note: candidate constructor not viable: requires 0 arguments, but 1 was provided
_LIBCPP_CONSTEXPR shared_ptr() _NOEXCEPT;
^
1 error generated.
Compiler exited with result code 1
MikeMB
  • 20,029
  • 9
  • 57
  • 102

2 Answers2

4

The standard (pre-C++17) only says the following about the 13th constructor:

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

But since std::unique_ptr<int[]>::pointer is convertible to int* the code should work. It's a bug in libc++.

In the meantime you can use the following:

auto sp = std::shared_ptr<int>(up.release(), up.get_deleter());
Emil Laine
  • 41,598
  • 9
  • 101
  • 157
  • Do you knwo by any chance if a bug report has been filed against that? – MikeMB Mar 06 '17 at 01:01
  • I have no idea. – Emil Laine Mar 06 '17 at 01:16
  • The C++17 standard seems to forbid it though, because `Y*` is not convertible to `T*`. AKA `int(*)[])` cannot be converted to `int*`. – EricWF Mar 06 '17 at 04:12
  • @EricWF: Thank you very much for taking care of this. Yes that is also my understanding of the situation in c++17. – MikeMB Mar 06 '17 at 07:02
  • @MikeMB Although the more I read the C++17 spec the more I think it's a standard defect. I'll be filing a LWG issue about that too. – EricWF Mar 06 '17 at 07:20
  • @EricWF I mean, you guys did just vote in [LWG 2786](https://timsong-cpp.github.io/lwg-issues/2786) like three days ago... – T.C. Mar 07 '17 at 05:52
  • @T.C Who are you calling "you guys"... And also these changes were voted in months ago. Not in Kona last week. – EricWF Mar 07 '17 at 09:04
  • @EricWF Are you not a committee member? And I'm fairly sure the pre-Kona working paper doesn't have the Annex C addition; the issue was moved in plenary in Kona. – T.C. Mar 07 '17 at 09:16
  • I'm a committee member that's also an undergraduate student. So my ability for travel is limited. And according to libc++'s implementation table these changes were adopted from the LFTS v2 spec during the Issaquah meeting as P0414R2. – EricWF Mar 07 '17 at 09:27
  • @EricWF Yes, the `shared_ptr` changes proper was merged in earlier, but the Annex C addition that calls out this specific case was moved in Kona last Friday. – T.C. Mar 07 '17 at 14:17
3

Your types are different: int[] is not int.

Zan Lynx
  • 53,022
  • 10
  • 79
  • 131
  • They don't have to be the same (the constructors are actually templated on the element type of the argument smartpointer). But I admit I'm not 100% sure if the wording allows the conversion from `T[]` to `T` – MikeMB Mar 06 '17 at 00:25
  • Pointers allocated using "new []" need to be deleted using "delete []" which is not what std::shared_ptr will do. Your example has UB and libc++ is being helpful in rejecting it. – EricWF Mar 06 '17 at 00:37
  • 3
    @EricWF It gets the correct deleter from the `unique_ptr`, so that's fine. – Baum mit Augen Mar 06 '17 at 00:39
  • @EricWF: You can store any deleter in a shared_ptr (it gets type erased) and the constructor should take care of that. From the linked website: *"Constructs a shared_ptr which manages the object currently managed by r. The deleter associated with r is stored for future deletion of the managed object."* – MikeMB Mar 06 '17 at 00:46
  • My mistake. Thanks for the correction. I'll file a libc++ bug and fix it when I get time. (EDIT: Assuming my reading of the standard agrees with your example). – EricWF Mar 06 '17 at 01:13