7

The following code fails to compile on gcc 5.3 with compiler error complaining that the copy constructor of the unique_ptr is somehow invoked. Can someone explain why this happens?

#include <iostream>
#include <memory>
#include <deque>

using Foo = std::deque<std::unique_ptr<int>>;                                   


void foo() {                                                                    
  std::vector<Foo> a;                                                           
  a.emplace_back();   // this fails to compile                                                             
}

The key line in the compiler error is:

gcc-4.9.2/include/c++/4.9.2/bits/stl_construct.h:75:7: error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp = std::default_delete<int>]’ { ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }

4
  • Oh yeah, this is a mess. Commented Apr 2, 2016 at 0:41
  • 2
    You should include <vector> and <deque>, not <queue>. Not that it matters here. Commented Apr 2, 2016 at 0:50
  • Thanks for pointing this out. Fixed. BTW, if we replace std::deque with std::queue, the code failed to compile for the same compiler error. Commented Apr 2, 2016 at 3:06
  • @BarryTheHatchet. Added the error message to the question. Thanks! Commented Apr 2, 2016 at 16:58

1 Answer 1

6
  • vector::emplace_back needs to handle reallocation.
  • on vector reallocation, the existing elements are moved if either

    • they cannot be copied (as determined by std::is_copy_constructible); or
    • their move constructor is noexcept.

    otherwise they are copied. This is to maintain the strong exception safety guarantee.

  • std::deque<std::unique_ptr<int>>'s move constructor is not noexcept (depending on the implementation, it may need to allocate memory).
  • std::deque<std::unique_ptr<int>> can be copied according to std::is_copy_constructible, because that only checks if there is a constructor with the right signature, not whether the body of said constructor will actually compile.
  • vector::emplace_back therefore attempts to copy it.
  • the compiler blows up.
Sign up to request clarification or add additional context in comments.

5 Comments

Could this be considered a defect in the language? Because this is absurd.
@BarryTheHatchet: Last time I poked about, it had something to do with stateful allocators. You can't tell at compile time if two allocators are equal, so you don't know at compile time if you can move or have to copy.
@MooingDuck That's a separate beast (move assignment).
@BarryTheHatchet Hard to fix because of custom allocator constructs, I think. You can't SFINAE on just is_copy_constructible etc. because construct can mutilate the arguments.
@T.C. Thank you so much! The explanation makes a lot of sense. I have to say this behavior is very surprising and the reasoning is very subtle, especially the point that deque move constructor is not noexcept.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.