{"id":102924,"date":"2019-09-26T07:00:00","date_gmt":"2019-09-26T14:00:00","guid":{"rendered":"http:\/\/devblogs.microsoft.com\/oldnewthing\/?p=102924"},"modified":"2019-09-25T22:08:09","modified_gmt":"2019-09-26T05:08:09","slug":"20190926-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20190926-00\/?p=102924","title":{"rendered":"Why does <CODE>std::is_copy_constructible<\/CODE> report that a vector of move-only objects is copy constructible?"},"content":{"rendered":"<p>The <code>std::is_<\/code><code>copy_<\/code><code>constructible<\/code> traits class reports whether a type is copy-constructible. But it sometimes reports that a type is copy-constructible even though it isn&#8217;t.<\/p>\n<pre style=\"white-space: pre-wrap;\">#include &lt;memory&gt;\r\n#include &lt;vector&gt;\r\n#include &lt;type_traits&gt;\r\n\r\n\/\/ unique_ptr is movable but not copyable.\r\nusing move_only = std::unique_ptr&lt;int&gt;;\r\n\r\n\/\/ This assertion succeeds\r\nstatic_assert(std::is_copy_constructible_v&lt;std::vector&lt;move_only&gt;&gt;);\r\n\r\n\/\/ But the type is not copy-constructible.\r\nvoid f(std::vector&lt;move_only&gt; v)\r\n{\r\n    auto copy = v; \/\/ long confusing error message\r\n}\r\n<\/pre>\n<p>The Visual C++ compiler&#8217;s error message is the most expansive, which doesn&#8217;t necessarily mean it&#8217;s the most helpful.<\/p>\n<pre style=\"white-space: pre-wrap;\">xmemory0(819): error C2280: 'std::unique_ptr&lt;int, std::default_delete&lt;_Ty&gt;&gt;::<wbr \/>unique_ptr(const std::unique_ptr&lt;_Ty, std::default_delete&lt;_Ty&gt;&gt; &amp;)': attempting to reference a deleted function\r\n        with\r\n        [\r\n            _Ty=int\r\n        ]\r\nmemory(1968): note: see declaration of 'std::unique_ptr&lt;int, std::default_delete&lt;_Ty&gt;&gt;::<wbr \/>unique_ptr'\r\n        with\r\n        [\r\n            _Ty=int\r\n        ]\r\nmemory(1968): note: 'std::unique_ptr&lt;int, std::default_delete&lt;_Ty&gt;&gt;::<wbr \/>unique_ptr(const std::unique_ptr&lt;_Ty, std::default_delete&lt;_Ty&gt;&gt; &amp;)': function was explicitly deleted\r\n        with\r\n        [\r\n            _Ty=int\r\n        ]\r\nxmemory(141): note: see reference to function template instantiation 'void std::_Default_allocator_traits&lt;_Alloc&gt;::construct&lt;_Ty, _Ty&amp;&gt;(_Alloc &amp;, _Objty *const , _Ty &amp;)' being compiled\r\n        with\r\n        [\r\n            _Alloc=std::allocator&lt;move_only&gt;,\r\n            _Ty=std::unique_ptr&lt;int, std::default_delete&lt;int&gt;&gt;,\r\n            _Objty=std::unique_ptr&lt;int, std::default_delete&lt;int&gt;&gt;\r\n        ]\r\nxmemory(142): note: see reference to function template instantiation 'void std::_Default_allocator_traits&lt;_Alloc&gt;::construct&lt;_Ty, _Ty&amp;&gt;(_Alloc &amp;, _Objty *const , _Ty &amp;)' being compiled\r\n        with\r\n        [\r\n            _Alloc=std::allocator&lt;move_only&gt;,\r\n            _Ty=std::unique_ptr&lt;int,std::default_delete&lt;int&gt;&gt;,\r\n            _Objty=std::unique_ptr&lt;int,std::default_delete&lt;int&gt;&gt;\r\n        ]\r\nxmemory(173): note: see reference to function template instantiation 'void std::_Uninitialized_backout_al&lt;_Ty *, _Alloc&gt;::<wbr \/>_Emplace_back&lt;_Ty&amp;&gt;(_Ty &amp;)' being compiled\r\n        with\r\n        [\r\n            _Ty=std::unique_ptr&lt;int,std::default_delete&lt;int&gt;&gt;,\r\n            _Alloc=std::allocator&lt;move_only&gt;\r\n        ]\r\nxmemory(173): note: see reference to function template instantiation 'void std::_Uninitialized_backout_al&lt;_Ty *, _Alloc&gt;::<wbr \/>_Emplace_back&lt;_Ty&amp;&gt;(_Ty &amp;)' being compiled\r\n        with\r\n        [\r\n            _Ty=std::unique_ptr&lt;int,std::default_delete&lt;int&gt;&gt;,\r\n            _Alloc=std::allocator&lt;move_only&gt;\r\n        ]\r\nvector(1444): note: see reference to function template instantiation '_NoThrowFwdIt *std::_Uninitialized_copy&lt;_Iter, std::unique_ptr&lt;int, std::default_delete&lt;_Ty&gt;&gt;*, std::allocator&lt;std::unique_ptr&lt;_Ty, std::default_delete&lt;_Ty&gt;&gt;&gt;&gt;(const _InIt, const _InIt, _NoThrowFwdIt, _Alloc &amp;)' being compiled\r\n        with\r\n        [\r\n            _NoThrowFwdIt=std::unique_ptr&lt;int,std::default_delete&lt;int&gt;&gt; *,\r\n            _Iter=std::unique_ptr&lt;int,std::default_delete&lt;int&gt;&gt; *,\r\n            _Ty=int,\r\n            _InIt=std::unique_ptr&lt;int,std::default_delete&lt;int&gt;&gt; *,\r\n            _Alloc=std::allocator&lt;move_only&gt;\r\n        ]\r\nvector(464): note: see reference to function template instantiation 'std::unique_ptr&lt;int, std::default_delete&lt;_Ty&gt;&gt; *std::vector&lt;move_only, std::allocator&lt;std::unique_ptr&lt;_Ty, std::default_delete&lt;_Ty&gt;&gt;&gt;&gt;::<wbr \/>_Ucopy&lt;std::unique_ptr&lt;_Ty, std::default_delete&lt;_Ty&gt;&gt;*&gt;(_Iter, _Iter, std::unique_ptr&lt;_Ty, std::default_delete&lt;_Ty&gt;&gt; *)' being compiled\r\n        with\r\n        [\r\n            _Ty=int,\r\n            _Iter=std::unique_ptr&lt;int,std::default_delete&lt;int&gt;&gt; *\r\n        ]\r\nvector(464): note: see reference to function template instantiation 'std::unique_ptr&lt;int, std::default_delete&lt;_Ty&gt;&gt; *std::vector&lt;move_only, std::allocator&lt;std::unique_ptr&lt;_Ty, std::default_delete&lt;_Ty&gt;&gt;&gt;&gt;::<wbr \/>_Ucopy&lt;std::unique_ptr&lt;_Ty, std::default_delete&lt;_Ty&gt;&gt;*&gt;(_Iter, _Iter, std::unique_ptr&lt;_Ty, std::default_delete&lt;_Ty&gt;&gt; *)' being compiled\r\n        with\r\n        [\r\n            _Ty=int,\r\n            _Iter=std::unique_ptr&lt;int,std::default_delete&lt;int&gt;&gt; *\r\n        ]\r\nvector(456): note: while compiling class template member function 'std::vector&lt;move_only, std::allocator&lt;_Ty&gt;&gt;::<wbr \/>vector(const std::vector&lt;_Ty, std::allocator&lt;_Ty&gt;&gt; &amp;)'\r\n        with\r\n        [\r\n            _Ty=move_only\r\n        ]\r\ntest.cpp(21): note: see reference to function template instantiation 'std::vector&lt;move_only, std::allocator&lt;_Ty&gt;&gt;::<wbr \/>vector(const std::vector&lt;_Ty, std::allocator&lt;_Ty&gt;&gt; &amp;)' being compiled\r\n        with\r\n        [\r\n            _Ty=move_only\r\n        ]\r\ntype_traits(694): note: see reference to class template instantiation 'std::vector&lt;move_only, std::allocator&lt;_Ty&gt;&gt;' being compiled\r\n        with\r\n        [\r\n            _Ty=move_only\r\n        ]\r\ntest.cpp(16): note: see reference to variable template 'const bool is_copy_constructible_v&lt;std::vector&lt;std::unique_ptr&lt;int, std::default_delete&lt;int&gt; &gt;, std::allocator&lt;std::unique_ptr&lt;int, std::default_delete&lt;int&gt; &gt; &gt; &gt; &gt;' being compiled\r\n<\/pre>\n<p>gcc and clang&#8217;s errors are roughly comparable. Here&#8217;s clang:<\/p>\n<pre style=\"white-space: pre-wrap;\">In file included from memory:64:\r\nstl_construct.h:75:38: error: call to deleted constructor of 'std::unique_ptr&lt;int, std::default_delete&lt;int&gt; &gt;'\r\n    { ::new(static_cast&lt;void*&gt;(__p)) _T1(std::forward&lt;_Args&gt;(__args)...); }\r\n                                     ^   ~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\nstl_uninitialized.h:83:8: note: in instantiation of function template specialization 'std::_Construct&lt;std::unique_ptr&lt;int, std::default_delete&lt;int&gt; &gt;, const std::unique_ptr&lt;int, std::default_delete&lt;int&gt; &gt; &amp;&gt;' requested here\r\n                std::_Construct(std::__addressof(*__cur), *__first);\r\n                     ^\r\nstl_uninitialized.h:134:2: note: in instantiation of function template specialization 'std::__uninitialized_copy&lt;false&gt;::__uninit_copy&lt;__gnu_cxx::__normal_iterator&lt;const std::unique_ptr&lt;int, std::default_delete&lt;int&gt; &gt; *, std::vector&lt;std::unique_ptr&lt;int, std::default_delete&lt;int&gt; &gt;, std::allocator&lt;std::unique_ptr&lt;int, std::default_delete&lt;int&gt; &gt; &gt; &gt; &gt;, std::unique_ptr&lt;int, std::default_delete&lt;int&gt; &gt; *&gt;' requested here\r\n        __uninit_copy(__first, __last, __result);\r\n        ^\r\nstl_uninitialized.h:289:19: note: in instantiation of function template specialization 'std::uninitialized_copy&lt;__gnu_cxx::__normal_iterator&lt;const std::unique_ptr&lt;int, std::default_delete&lt;int&gt; &gt; *, std::vector&lt;std::unique_ptr&lt;int, std::default_delete&lt;int&gt; &gt;, std::allocator&lt;std::unique_ptr&lt;int, std::default_delete&lt;int&gt; &gt; &gt; &gt; &gt;, std::unique_ptr&lt;int, std::default_delete&lt;int&gt; &gt; *&gt;' requested here\r\n    { return std::uninitialized_copy(__first, __last, __result); }\r\n                  ^\r\nstl_vector.h:463:9: note: in instantiation of function template specialization 'std::__uninitialized_copy_a&lt;__gnu_cxx::__normal_iterator&lt;const std::unique_ptr&lt;int, std::default_delete&lt;int&gt; &gt; *, std::vector&lt;std::unique_ptr&lt;int, std::default_delete&lt;int&gt; &gt;, std::allocator&lt;std::unique_ptr&lt;int, std::default_delete&lt;int&gt; &gt; &gt; &gt; &gt;, std::unique_ptr&lt;int, std::default_delete&lt;int&gt; &gt; *, std::unique_ptr&lt;int, std::default_delete&lt;int&gt; &gt; &gt;' requested here\r\n          std::__uninitialized_copy_a(__x.begin(), __x.end(),\r\n               ^\r\ntest.cpp:21:13: note: in instantiation of member function 'std::vector&lt;std::unique_ptr&lt;int, std::default_delete&lt;int&gt; &gt;, std::allocator&lt;std::unique_ptr&lt;int, std::default_delete&lt;int&gt; &gt; &gt; &gt;::vector' requested here\r\nauto copy = v;\r\n            ^\r\nunique_ptr.h:394:7: note: 'unique_ptr' has been explicitly marked deleted here\r\n      unique_ptr(const unique_ptr&amp;) = delete;\r\n      ^\r\n<\/pre>\n<p>and here&#8217;s gcc:<\/p>\n<pre style=\"white-space: pre-wrap;\">In file included from memory:65,\r\n                 from test.cpp:2:\r\nstl_construct.h: In instantiation of 'void std::_Construct(_T1*, _Args&amp;&amp; ...) [with _T1 = std::unique_ptr&lt;int&gt;; _Args = {const std::unique_ptr&lt;int, std::default_delete&lt;int&gt; &gt;&amp;}]':\r\nstl_uninitialized.h:89:18:   required from 'static _ForwardIterator std::__uninitialized_copy&lt;_TrivialValueTypes&gt;::__uninit_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = __gnu_cxx::__normal_iterator&lt;const std::unique_ptr&lt;int&gt;*, std::vector&lt;std::unique_ptr&lt;int&gt; &gt; &gt;; _ForwardIterator = std::unique_ptr&lt;int&gt;*; bool _TrivialValueTypes = false]'\r\nstl_uninitialized.h:142:15:   required from '_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = __gnu_cxx::__normal_iterator&lt;const std::unique_ptr&lt;int&gt;*, std::vector&lt;std::unique_ptr&lt;int&gt; &gt; &gt;; _ForwardIterator = std::unique_ptr&lt;int&gt;*]'\r\nstl_uninitialized.h:305:37:   required from '_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator&lt;_Tp&gt;&amp;) [with _InputIterator = __gnu_cxx::__normal_iterator&lt;const std::unique_ptr&lt;int&gt;*, std::vector&lt;std::unique_ptr&lt;int&gt; &gt; &gt;; _ForwardIterator = std::unique_ptr&lt;int&gt;*; _Tp = std::unique_ptr&lt;int&gt;]'\r\nstl_vector.h:555:31:   required from 'std::vector&lt;_Tp, _Alloc&gt;::vector(const std::vector&lt;_Tp, _Alloc&gt;&amp;) [with _Tp = std::unique_ptr&lt;int&gt;; _Alloc = std::allocator&lt;std::unique_ptr&lt;int&gt; &gt;]'\r\ntest.cpp:21:13:   required from here\r\nstl_construct.h:75:7: error: use of deleted function 'std::unique_ptr&lt;_Tp, _Dp&gt;::unique_ptr(const std::unique_ptr&lt;_Tp, _Dp&gt;&amp;) [with _Tp = int; _Dp = std::default_delete&lt;int&gt;]'\r\n   75 |     { ::new(static_cast&lt;void*&gt;(__p)) _T1(std::forward&lt;_Args&gt;(__args)...); }\r\n      |       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\nIn file included from memory:81,\r\n                 from test.cpp:2:\r\nunique_ptr.h:461:7: note: declared here\r\n  461 |       unique_ptr(const unique_ptr&amp;) = delete;\r\n      |       ^~~~~~~~~~\r\n<\/pre>\n<p>The gcc and clang errors are roughly the same, just in reverse order. gcc&#8217;s message ordering is frustrating because it spends so much time setting the scene that you want to interrupt it and say <i>get to the point already I don&#8217;t have all day<\/i>.<\/p>\n<p>Anyway, the deal is that a vector of move-only objects is not copyable because the contents of the vector cannot be copied. So why did <code>std::is_<\/code><code>copy_<\/code><code>constructible<\/code> say that it was copyable?<\/p>\n<p>Because <code>std::is_<\/code><code>copy_<\/code><code>constructible<\/code> looks at whether the class has a copy constructor, and <code>std::<\/code><code>vector<\/code> has a copy constructor. The copy constructor doesn&#8217;t compile if you have a vector of move-only objects, but <code>std::is_<\/code><code>copy_<\/code><code>constructible<\/code> doesn&#8217;t try to compile the copy constructor. It just checks whether the copy constructor exists.<\/p>\n<p>You can&#8217;t really expect <code>std::is_<\/code><code>copy_<\/code><code>constructible<\/code> to try to compile the copy constructor, because the copy constructor&#8217;s definition may not be visible at the time you ask.<\/p>\n<pre>struct copyable\r\n{\r\n  copyable();\r\n  copyable(const copyable&amp;);\r\n};\r\n<\/pre>\n<p>The <code>copyable<\/code> class claims to be copyable, but how do we know that its copy constructor will compile successfully? There&#8217;s no way to know, because there is no definition visible. We have to go by what it says on the tin, and the tin says that it&#8217;s copyable.<\/p>\n<p>The <code>std::vector<\/code> and other collections claim to be copyable, but whethey they actually <i>are<\/i> copyable depends on what you put into them.<\/p>\n<p>We&#8217;ll investigate one of the consequences of this &#8220;Trust what it says on the tin&#8221; behavior next time.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Because it has a copy constructor, says so on the tin, even though the constructor itself doesn&#8217;t compile.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-102924","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Because it has a copy constructor, says so on the tin, even though the constructor itself doesn&#8217;t compile.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/102924","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=102924"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/102924\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=102924"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=102924"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=102924"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}