Does Visual Studio 2017 need an explicit move constructor declaration?

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP











up vote
11
down vote

favorite
4












The below code can be compiled successfully using Visual Studio 2015, but it failed using Visual Studio 2017. Visual Studio 2017 reports:




error C2280: “std::pair::pair(const std::pair &)”: attempting to reference a deleted function




Code



#include <unordered_map>
#include <memory>

struct Node

std::unordered_map<int, std::unique_ptr<int>> map_;
// Uncommenting the following two lines will pass Visual Studio 2017 compilation
//Node(Node&& o) = default;
//Node() = default;
;

int main()

std::vector<Node> vec;
Node node;
vec.push_back(std::move(node));
return 0;



It looks like Visual Studio 2017 explicit needs a move constructor declaration. What is the reason?










share|improve this question









New contributor




finn is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.



















  • You're missing #include <vector> but as far as I can tell, that code should compile (and it does on e.g. GCC 8.2). Do you have the latest and greatest VS2017?
    – rubenvb
    6 hours ago











  • I can confirm this code fails with error code under vs2017 15.4.2.
    – marcinj
    6 hours ago














up vote
11
down vote

favorite
4












The below code can be compiled successfully using Visual Studio 2015, but it failed using Visual Studio 2017. Visual Studio 2017 reports:




error C2280: “std::pair::pair(const std::pair &)”: attempting to reference a deleted function




Code



#include <unordered_map>
#include <memory>

struct Node

std::unordered_map<int, std::unique_ptr<int>> map_;
// Uncommenting the following two lines will pass Visual Studio 2017 compilation
//Node(Node&& o) = default;
//Node() = default;
;

int main()

std::vector<Node> vec;
Node node;
vec.push_back(std::move(node));
return 0;



It looks like Visual Studio 2017 explicit needs a move constructor declaration. What is the reason?










share|improve this question









New contributor




finn is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.



















  • You're missing #include <vector> but as far as I can tell, that code should compile (and it does on e.g. GCC 8.2). Do you have the latest and greatest VS2017?
    – rubenvb
    6 hours ago











  • I can confirm this code fails with error code under vs2017 15.4.2.
    – marcinj
    6 hours ago












up vote
11
down vote

favorite
4









up vote
11
down vote

favorite
4






4





The below code can be compiled successfully using Visual Studio 2015, but it failed using Visual Studio 2017. Visual Studio 2017 reports:




error C2280: “std::pair::pair(const std::pair &)”: attempting to reference a deleted function




Code



#include <unordered_map>
#include <memory>

struct Node

std::unordered_map<int, std::unique_ptr<int>> map_;
// Uncommenting the following two lines will pass Visual Studio 2017 compilation
//Node(Node&& o) = default;
//Node() = default;
;

int main()

std::vector<Node> vec;
Node node;
vec.push_back(std::move(node));
return 0;



It looks like Visual Studio 2017 explicit needs a move constructor declaration. What is the reason?










share|improve this question









New contributor




finn is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.











The below code can be compiled successfully using Visual Studio 2015, but it failed using Visual Studio 2017. Visual Studio 2017 reports:




error C2280: “std::pair::pair(const std::pair &)”: attempting to reference a deleted function




Code



#include <unordered_map>
#include <memory>

struct Node

std::unordered_map<int, std::unique_ptr<int>> map_;
// Uncommenting the following two lines will pass Visual Studio 2017 compilation
//Node(Node&& o) = default;
//Node() = default;
;

int main()

std::vector<Node> vec;
Node node;
vec.push_back(std::move(node));
return 0;



It looks like Visual Studio 2017 explicit needs a move constructor declaration. What is the reason?







c++ visual-studio-2017 move-constructor






share|improve this question









New contributor




finn is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.











share|improve this question









New contributor




finn is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.









share|improve this question




share|improve this question








edited 15 mins ago









Sombrero Chicken

22.3k32772




22.3k32772






New contributor




finn is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.









asked 6 hours ago









finn

592




592




New contributor




finn is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.





New contributor





finn is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.






finn is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.











  • You're missing #include <vector> but as far as I can tell, that code should compile (and it does on e.g. GCC 8.2). Do you have the latest and greatest VS2017?
    – rubenvb
    6 hours ago











  • I can confirm this code fails with error code under vs2017 15.4.2.
    – marcinj
    6 hours ago
















  • You're missing #include <vector> but as far as I can tell, that code should compile (and it does on e.g. GCC 8.2). Do you have the latest and greatest VS2017?
    – rubenvb
    6 hours ago











  • I can confirm this code fails with error code under vs2017 15.4.2.
    – marcinj
    6 hours ago















You're missing #include <vector> but as far as I can tell, that code should compile (and it does on e.g. GCC 8.2). Do you have the latest and greatest VS2017?
– rubenvb
6 hours ago





You're missing #include <vector> but as far as I can tell, that code should compile (and it does on e.g. GCC 8.2). Do you have the latest and greatest VS2017?
– rubenvb
6 hours ago













I can confirm this code fails with error code under vs2017 15.4.2.
– marcinj
6 hours ago




I can confirm this code fails with error code under vs2017 15.4.2.
– marcinj
6 hours ago












3 Answers
3






active

oldest

votes

















up vote
7
down vote













Minimal example:



#include <memory>
#include <unordered_map>
#include <vector>

int main()
std::vector<std::unordered_map<int, std::unique_ptr<int>>> vec;
vec.reserve(1);



Live demo on GodBolt: https://godbolt.org/z/VApPkH.




Another example:



std::unordered_map<int, std::unique_ptr<int>> m;
auto m2 = std::move(m); // ok
auto m3 = std::move_if_noexcept(m); // error C2280



UPDATE



I believe the compilation error is legal. Vector's reallocation function can transfer (contents of) elements by using std::move_if_noexcept, therefore preferring copy constructors to throwing move constructors.



In libstdc++ (GCC) / libc++ (clang), move constructor of std::unordered_map is (seemingly) noexcept. Consequently, move constructor of Node is noexcept as well, and its copy constructor is not at all involved.



On the other hand, implementation from MSVC 2017 seemingly does not specify move constructor of std::unordered_map as noexcept. Therefore, move constructor of Node is not noexcept as well, and vector's reallocation function via std::move_if_noexcept tries to invoke copy constructor of Node.



Copy constructor of Node is implicitly defined such that is invokes copy constructor of std::unordered_map. However, the latter may not be invoked here, since the value type of map (std::pair<const int, std::unique_ptr<int>> in this case) is not copyable.



Finally, if you user-define move constructor of Node, its implicitly declared copy constructor is defined as deleted. And, IIRC, deleted implicitly declared copy constructor does not participate in overload resolution. But, the deleted copy constructor is not considered by std::move_if_noexcept, therefore it will use throwing move constructor of Node.






share|improve this answer






















  • Is this realy a bug? std::is_copy_constructible_v<std::unordered_map<int, std::unique_ptr<int>>> is true even if any attempt to copy such a type will result in a compilation error. Moreover it will also be detected as CopyInsertable, even if any try to instantiate the code to copy insert it will result in a compilation error.
    – Oliv
    2 hours ago










  • @Oliv That's a good question. It seems that the problem is that the move constructor of std::unordered_map is not noexcept in MSVC 2017 (which is seemingly not required by the Standard, or is it?). In libstdc++/libc++, it is noexcept.
    – Daniel Langr
    1 hour ago











  • @Oliv I updated my answer, I guess you are right. I even don't think now that it is "surprising".
    – Daniel Langr
    1 hour ago











  • @Oliv Thanks for clarificaton, I rephrased my answer not to mention CopyInsertable requirement. Important is, that std::unordered_map defines copy constructor, just it may not be invoked if the value type cannot be copied.
    – Daniel Langr
    1 hour ago










  • I think that we have excavated a rule to apply when we will use concepts. That is to make sure that the interface of a type really reflects its requirements. For example here the copy constructor of the unordered_map should have a constraint requires is_copy_constructible_v<element_type> or equivalent. I don't know how to name it, maybe rule concept propagation rule or maybe concept explicity rule ...??
    – Oliv
    3 mins ago

















up vote
5
down vote













When you declare a move constructor, the implicitly declared copy constructor is defined as deleted. On the other hand, when you don't declare a move constructor, the compiler implicitly defines the copy constructor when it need it. And this implicit definition is ill-formed.



unique_ptr is not CopyInsertable in a container that uses a standard allocator because it is not copy constructible so the copy constructor of map_ is ill-formed (it could have been declared as deleted, but this is not required by the standard).



As your example code show us, with newer version of MSVC, this ill-formed definition is generated with this example code. I do not think there is something in the standard that forbids it (even if this is realy surprising).



So you should indeed ensure that the copy constructor of Node is declared or implicitly defined as deleted.






share|improve this answer


















  • 1




    Im wondering as to why the issue is not seen with clang or gcc ?
    – darune
    4 hours ago






  • 1




    @darune I think the bug is in the standard requirement. If a standard type is not copy constructible it should be possible to check it with traits. But actually implementing such a requirement would be a real pain. I hope that with concepts this will be fixed.
    – Oliv
    3 hours ago






  • 1




    I don't have VS2017 accessible, but couldn't be the problem with that implicitly-declared move constructor of std::unordered_map (and therefore of Node) is not noexcept? Then, push_back, i.e., emplace_back, may prefer copy constructor (during reallocation) than potentially throwing move constructor (std::move_if_noexcept)? With user-declared move constructor, the copy constructor is deleted, so throwing move constructor will be used.
    – Daniel Langr
    3 hours ago







  • 3




    The type traits magic to do this "correctly" is surprisingly complex. I implemented an open addressing hash map (and set) that is mostly API-compatible with unordered_map, and ended up having to use private inheritance from std::conditional<std::is_copy_constructible<...>::value && ..., AllowCopy, DisallowCopy>::type where AllowCopy is empty and DisallowCopy has deleted copy operations (among other things). I also check for default c'tible allocator, and all of this is still not quite right, e.g. I support neither allocator propagation on copy nor copying of elements in the allocator.
    – Arne Vogel
    2 hours ago






  • 2




    is_nothrow_move_constructible_v<Node> is false in MSVC, so it tries to copy and fails; it is true in gcc, so it moves. If is_nothrow_move_constructible_v<Node> is forced to become false in gcc, it also tries to copy and fails. Why do MSVC and gcc disagree on is_nothrow_move_constructible_v<Node> value?
    – Evg
    1 hour ago

















up vote
3
down vote













Let's look at the std::vector source code (I replaced pointer and _Ty with actual types):



void _Umove_if_noexcept1(Node* First, Node* Last, Node* Dest, true_type)
// move [First, Last) to raw Dest, using allocator
_Uninitialized_move(First, Last, Dest, this->_Getal());


void _Umove_if_noexcept1(Node* First, Node* Last, Node* Dest, false_type)
// copy [First, Last) to raw Dest, using allocator
_Uninitialized_copy(First, Last, Dest, this->_Getal());


void _Umove_if_noexcept(Node* First, Node* Last, Node* Dest)
// move_if_noexcept [First, Last) to raw Dest, using allocator
_Umove_if_noexcept1(First, Last, Dest,
bool_constant<disjunction_v<is_nothrow_move_constructible<Node>, negation<is_copy_constructible<Node>>>>);



If Node is no-throw move-constructible or is not copy-constructible, _Uninitialized_move is called, otherwise, _Uninitialized_copy is called.



The problem is that the type trait std::is_copy_constructible_v is true for Node if you do not declare a move constructor explicitly. This declaration makes copy-constructor deleted.



libstdc++ implements std::vector in a similar way, but there std::is_nothrow_move_constructible_v<Node> is true in contrast to MSVC, where it is false. So, move semantics is used and the compiler does not try to generate the copy-constructor.



But if we force is_nothrow_move_constructible_v to become false



struct Base 
Base() = default;
Base(const S&) = default;
Base(Base&&) noexcept(false)
;

struct Node : Base
std::unordered_map<int, std::unique_ptr<int>> map;
;

int main()
std::vector<Node> vec;
vec.reserve(1);



the same error occurs:



/usr/include/c++/7/ext/new_allocator.h:136:4: error: use of deleted function ‘std::pair<_T1, _T2>::pair(const std::pair<_T1, _T2>&) [with _T1 = const int; _T2 = std::unique_ptr<int>]’
::new((void *)__p) _Up(std::forward<_Args>(__args)...);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~





share|improve this answer


















  • 1




    Im wondering as to why the issue is not seen with clang or gcc ?
    – darune
    4 hours ago






  • 1




    @darune, the dumb answer is because gcc's and clang's implementations do not rely on std::is_copy_constructible in this particular piece of code. I don't have a better answer now.
    – Evg
    3 hours ago










  • If i understand you correctly that code is in the MSVC vector implementation (?) and isn't that a bug in this case ? It does feel like like a bug, because invoking the move constructor directly does compile fine.
    – darune
    3 hours ago











  • @darune, yes, the code in my answer is directly from MSVC STL implementation. I agree that it feels like a bug.
    – Evg
    3 hours ago










  • @darune, added some info about in libstdc++.
    – Evg
    2 hours ago










Your Answer






StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");

StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);






finn is a new contributor. Be nice, and check out our Code of Conduct.









 

draft saved


draft discarded


















StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53168836%2fdoes-visual-studio-2017-need-an-explicit-move-constructor-declaration%23new-answer', 'question_page');

);

Post as a guest






























3 Answers
3






active

oldest

votes








3 Answers
3






active

oldest

votes









active

oldest

votes






active

oldest

votes








up vote
7
down vote













Minimal example:



#include <memory>
#include <unordered_map>
#include <vector>

int main()
std::vector<std::unordered_map<int, std::unique_ptr<int>>> vec;
vec.reserve(1);



Live demo on GodBolt: https://godbolt.org/z/VApPkH.




Another example:



std::unordered_map<int, std::unique_ptr<int>> m;
auto m2 = std::move(m); // ok
auto m3 = std::move_if_noexcept(m); // error C2280



UPDATE



I believe the compilation error is legal. Vector's reallocation function can transfer (contents of) elements by using std::move_if_noexcept, therefore preferring copy constructors to throwing move constructors.



In libstdc++ (GCC) / libc++ (clang), move constructor of std::unordered_map is (seemingly) noexcept. Consequently, move constructor of Node is noexcept as well, and its copy constructor is not at all involved.



On the other hand, implementation from MSVC 2017 seemingly does not specify move constructor of std::unordered_map as noexcept. Therefore, move constructor of Node is not noexcept as well, and vector's reallocation function via std::move_if_noexcept tries to invoke copy constructor of Node.



Copy constructor of Node is implicitly defined such that is invokes copy constructor of std::unordered_map. However, the latter may not be invoked here, since the value type of map (std::pair<const int, std::unique_ptr<int>> in this case) is not copyable.



Finally, if you user-define move constructor of Node, its implicitly declared copy constructor is defined as deleted. And, IIRC, deleted implicitly declared copy constructor does not participate in overload resolution. But, the deleted copy constructor is not considered by std::move_if_noexcept, therefore it will use throwing move constructor of Node.






share|improve this answer






















  • Is this realy a bug? std::is_copy_constructible_v<std::unordered_map<int, std::unique_ptr<int>>> is true even if any attempt to copy such a type will result in a compilation error. Moreover it will also be detected as CopyInsertable, even if any try to instantiate the code to copy insert it will result in a compilation error.
    – Oliv
    2 hours ago










  • @Oliv That's a good question. It seems that the problem is that the move constructor of std::unordered_map is not noexcept in MSVC 2017 (which is seemingly not required by the Standard, or is it?). In libstdc++/libc++, it is noexcept.
    – Daniel Langr
    1 hour ago











  • @Oliv I updated my answer, I guess you are right. I even don't think now that it is "surprising".
    – Daniel Langr
    1 hour ago











  • @Oliv Thanks for clarificaton, I rephrased my answer not to mention CopyInsertable requirement. Important is, that std::unordered_map defines copy constructor, just it may not be invoked if the value type cannot be copied.
    – Daniel Langr
    1 hour ago










  • I think that we have excavated a rule to apply when we will use concepts. That is to make sure that the interface of a type really reflects its requirements. For example here the copy constructor of the unordered_map should have a constraint requires is_copy_constructible_v<element_type> or equivalent. I don't know how to name it, maybe rule concept propagation rule or maybe concept explicity rule ...??
    – Oliv
    3 mins ago














up vote
7
down vote













Minimal example:



#include <memory>
#include <unordered_map>
#include <vector>

int main()
std::vector<std::unordered_map<int, std::unique_ptr<int>>> vec;
vec.reserve(1);



Live demo on GodBolt: https://godbolt.org/z/VApPkH.




Another example:



std::unordered_map<int, std::unique_ptr<int>> m;
auto m2 = std::move(m); // ok
auto m3 = std::move_if_noexcept(m); // error C2280



UPDATE



I believe the compilation error is legal. Vector's reallocation function can transfer (contents of) elements by using std::move_if_noexcept, therefore preferring copy constructors to throwing move constructors.



In libstdc++ (GCC) / libc++ (clang), move constructor of std::unordered_map is (seemingly) noexcept. Consequently, move constructor of Node is noexcept as well, and its copy constructor is not at all involved.



On the other hand, implementation from MSVC 2017 seemingly does not specify move constructor of std::unordered_map as noexcept. Therefore, move constructor of Node is not noexcept as well, and vector's reallocation function via std::move_if_noexcept tries to invoke copy constructor of Node.



Copy constructor of Node is implicitly defined such that is invokes copy constructor of std::unordered_map. However, the latter may not be invoked here, since the value type of map (std::pair<const int, std::unique_ptr<int>> in this case) is not copyable.



Finally, if you user-define move constructor of Node, its implicitly declared copy constructor is defined as deleted. And, IIRC, deleted implicitly declared copy constructor does not participate in overload resolution. But, the deleted copy constructor is not considered by std::move_if_noexcept, therefore it will use throwing move constructor of Node.






share|improve this answer






















  • Is this realy a bug? std::is_copy_constructible_v<std::unordered_map<int, std::unique_ptr<int>>> is true even if any attempt to copy such a type will result in a compilation error. Moreover it will also be detected as CopyInsertable, even if any try to instantiate the code to copy insert it will result in a compilation error.
    – Oliv
    2 hours ago










  • @Oliv That's a good question. It seems that the problem is that the move constructor of std::unordered_map is not noexcept in MSVC 2017 (which is seemingly not required by the Standard, or is it?). In libstdc++/libc++, it is noexcept.
    – Daniel Langr
    1 hour ago











  • @Oliv I updated my answer, I guess you are right. I even don't think now that it is "surprising".
    – Daniel Langr
    1 hour ago











  • @Oliv Thanks for clarificaton, I rephrased my answer not to mention CopyInsertable requirement. Important is, that std::unordered_map defines copy constructor, just it may not be invoked if the value type cannot be copied.
    – Daniel Langr
    1 hour ago










  • I think that we have excavated a rule to apply when we will use concepts. That is to make sure that the interface of a type really reflects its requirements. For example here the copy constructor of the unordered_map should have a constraint requires is_copy_constructible_v<element_type> or equivalent. I don't know how to name it, maybe rule concept propagation rule or maybe concept explicity rule ...??
    – Oliv
    3 mins ago












up vote
7
down vote










up vote
7
down vote









Minimal example:



#include <memory>
#include <unordered_map>
#include <vector>

int main()
std::vector<std::unordered_map<int, std::unique_ptr<int>>> vec;
vec.reserve(1);



Live demo on GodBolt: https://godbolt.org/z/VApPkH.




Another example:



std::unordered_map<int, std::unique_ptr<int>> m;
auto m2 = std::move(m); // ok
auto m3 = std::move_if_noexcept(m); // error C2280



UPDATE



I believe the compilation error is legal. Vector's reallocation function can transfer (contents of) elements by using std::move_if_noexcept, therefore preferring copy constructors to throwing move constructors.



In libstdc++ (GCC) / libc++ (clang), move constructor of std::unordered_map is (seemingly) noexcept. Consequently, move constructor of Node is noexcept as well, and its copy constructor is not at all involved.



On the other hand, implementation from MSVC 2017 seemingly does not specify move constructor of std::unordered_map as noexcept. Therefore, move constructor of Node is not noexcept as well, and vector's reallocation function via std::move_if_noexcept tries to invoke copy constructor of Node.



Copy constructor of Node is implicitly defined such that is invokes copy constructor of std::unordered_map. However, the latter may not be invoked here, since the value type of map (std::pair<const int, std::unique_ptr<int>> in this case) is not copyable.



Finally, if you user-define move constructor of Node, its implicitly declared copy constructor is defined as deleted. And, IIRC, deleted implicitly declared copy constructor does not participate in overload resolution. But, the deleted copy constructor is not considered by std::move_if_noexcept, therefore it will use throwing move constructor of Node.






share|improve this answer














Minimal example:



#include <memory>
#include <unordered_map>
#include <vector>

int main()
std::vector<std::unordered_map<int, std::unique_ptr<int>>> vec;
vec.reserve(1);



Live demo on GodBolt: https://godbolt.org/z/VApPkH.




Another example:



std::unordered_map<int, std::unique_ptr<int>> m;
auto m2 = std::move(m); // ok
auto m3 = std::move_if_noexcept(m); // error C2280



UPDATE



I believe the compilation error is legal. Vector's reallocation function can transfer (contents of) elements by using std::move_if_noexcept, therefore preferring copy constructors to throwing move constructors.



In libstdc++ (GCC) / libc++ (clang), move constructor of std::unordered_map is (seemingly) noexcept. Consequently, move constructor of Node is noexcept as well, and its copy constructor is not at all involved.



On the other hand, implementation from MSVC 2017 seemingly does not specify move constructor of std::unordered_map as noexcept. Therefore, move constructor of Node is not noexcept as well, and vector's reallocation function via std::move_if_noexcept tries to invoke copy constructor of Node.



Copy constructor of Node is implicitly defined such that is invokes copy constructor of std::unordered_map. However, the latter may not be invoked here, since the value type of map (std::pair<const int, std::unique_ptr<int>> in this case) is not copyable.



Finally, if you user-define move constructor of Node, its implicitly declared copy constructor is defined as deleted. And, IIRC, deleted implicitly declared copy constructor does not participate in overload resolution. But, the deleted copy constructor is not considered by std::move_if_noexcept, therefore it will use throwing move constructor of Node.







share|improve this answer














share|improve this answer



share|improve this answer








edited 41 mins ago

























answered 2 hours ago









Daniel Langr

6,0142243




6,0142243











  • Is this realy a bug? std::is_copy_constructible_v<std::unordered_map<int, std::unique_ptr<int>>> is true even if any attempt to copy such a type will result in a compilation error. Moreover it will also be detected as CopyInsertable, even if any try to instantiate the code to copy insert it will result in a compilation error.
    – Oliv
    2 hours ago










  • @Oliv That's a good question. It seems that the problem is that the move constructor of std::unordered_map is not noexcept in MSVC 2017 (which is seemingly not required by the Standard, or is it?). In libstdc++/libc++, it is noexcept.
    – Daniel Langr
    1 hour ago











  • @Oliv I updated my answer, I guess you are right. I even don't think now that it is "surprising".
    – Daniel Langr
    1 hour ago











  • @Oliv Thanks for clarificaton, I rephrased my answer not to mention CopyInsertable requirement. Important is, that std::unordered_map defines copy constructor, just it may not be invoked if the value type cannot be copied.
    – Daniel Langr
    1 hour ago










  • I think that we have excavated a rule to apply when we will use concepts. That is to make sure that the interface of a type really reflects its requirements. For example here the copy constructor of the unordered_map should have a constraint requires is_copy_constructible_v<element_type> or equivalent. I don't know how to name it, maybe rule concept propagation rule or maybe concept explicity rule ...??
    – Oliv
    3 mins ago
















  • Is this realy a bug? std::is_copy_constructible_v<std::unordered_map<int, std::unique_ptr<int>>> is true even if any attempt to copy such a type will result in a compilation error. Moreover it will also be detected as CopyInsertable, even if any try to instantiate the code to copy insert it will result in a compilation error.
    – Oliv
    2 hours ago










  • @Oliv That's a good question. It seems that the problem is that the move constructor of std::unordered_map is not noexcept in MSVC 2017 (which is seemingly not required by the Standard, or is it?). In libstdc++/libc++, it is noexcept.
    – Daniel Langr
    1 hour ago











  • @Oliv I updated my answer, I guess you are right. I even don't think now that it is "surprising".
    – Daniel Langr
    1 hour ago











  • @Oliv Thanks for clarificaton, I rephrased my answer not to mention CopyInsertable requirement. Important is, that std::unordered_map defines copy constructor, just it may not be invoked if the value type cannot be copied.
    – Daniel Langr
    1 hour ago










  • I think that we have excavated a rule to apply when we will use concepts. That is to make sure that the interface of a type really reflects its requirements. For example here the copy constructor of the unordered_map should have a constraint requires is_copy_constructible_v<element_type> or equivalent. I don't know how to name it, maybe rule concept propagation rule or maybe concept explicity rule ...??
    – Oliv
    3 mins ago















Is this realy a bug? std::is_copy_constructible_v<std::unordered_map<int, std::unique_ptr<int>>> is true even if any attempt to copy such a type will result in a compilation error. Moreover it will also be detected as CopyInsertable, even if any try to instantiate the code to copy insert it will result in a compilation error.
– Oliv
2 hours ago




Is this realy a bug? std::is_copy_constructible_v<std::unordered_map<int, std::unique_ptr<int>>> is true even if any attempt to copy such a type will result in a compilation error. Moreover it will also be detected as CopyInsertable, even if any try to instantiate the code to copy insert it will result in a compilation error.
– Oliv
2 hours ago












@Oliv That's a good question. It seems that the problem is that the move constructor of std::unordered_map is not noexcept in MSVC 2017 (which is seemingly not required by the Standard, or is it?). In libstdc++/libc++, it is noexcept.
– Daniel Langr
1 hour ago





@Oliv That's a good question. It seems that the problem is that the move constructor of std::unordered_map is not noexcept in MSVC 2017 (which is seemingly not required by the Standard, or is it?). In libstdc++/libc++, it is noexcept.
– Daniel Langr
1 hour ago













@Oliv I updated my answer, I guess you are right. I even don't think now that it is "surprising".
– Daniel Langr
1 hour ago





@Oliv I updated my answer, I guess you are right. I even don't think now that it is "surprising".
– Daniel Langr
1 hour ago













@Oliv Thanks for clarificaton, I rephrased my answer not to mention CopyInsertable requirement. Important is, that std::unordered_map defines copy constructor, just it may not be invoked if the value type cannot be copied.
– Daniel Langr
1 hour ago




@Oliv Thanks for clarificaton, I rephrased my answer not to mention CopyInsertable requirement. Important is, that std::unordered_map defines copy constructor, just it may not be invoked if the value type cannot be copied.
– Daniel Langr
1 hour ago












I think that we have excavated a rule to apply when we will use concepts. That is to make sure that the interface of a type really reflects its requirements. For example here the copy constructor of the unordered_map should have a constraint requires is_copy_constructible_v<element_type> or equivalent. I don't know how to name it, maybe rule concept propagation rule or maybe concept explicity rule ...??
– Oliv
3 mins ago




I think that we have excavated a rule to apply when we will use concepts. That is to make sure that the interface of a type really reflects its requirements. For example here the copy constructor of the unordered_map should have a constraint requires is_copy_constructible_v<element_type> or equivalent. I don't know how to name it, maybe rule concept propagation rule or maybe concept explicity rule ...??
– Oliv
3 mins ago












up vote
5
down vote













When you declare a move constructor, the implicitly declared copy constructor is defined as deleted. On the other hand, when you don't declare a move constructor, the compiler implicitly defines the copy constructor when it need it. And this implicit definition is ill-formed.



unique_ptr is not CopyInsertable in a container that uses a standard allocator because it is not copy constructible so the copy constructor of map_ is ill-formed (it could have been declared as deleted, but this is not required by the standard).



As your example code show us, with newer version of MSVC, this ill-formed definition is generated with this example code. I do not think there is something in the standard that forbids it (even if this is realy surprising).



So you should indeed ensure that the copy constructor of Node is declared or implicitly defined as deleted.






share|improve this answer


















  • 1




    Im wondering as to why the issue is not seen with clang or gcc ?
    – darune
    4 hours ago






  • 1




    @darune I think the bug is in the standard requirement. If a standard type is not copy constructible it should be possible to check it with traits. But actually implementing such a requirement would be a real pain. I hope that with concepts this will be fixed.
    – Oliv
    3 hours ago






  • 1




    I don't have VS2017 accessible, but couldn't be the problem with that implicitly-declared move constructor of std::unordered_map (and therefore of Node) is not noexcept? Then, push_back, i.e., emplace_back, may prefer copy constructor (during reallocation) than potentially throwing move constructor (std::move_if_noexcept)? With user-declared move constructor, the copy constructor is deleted, so throwing move constructor will be used.
    – Daniel Langr
    3 hours ago







  • 3




    The type traits magic to do this "correctly" is surprisingly complex. I implemented an open addressing hash map (and set) that is mostly API-compatible with unordered_map, and ended up having to use private inheritance from std::conditional<std::is_copy_constructible<...>::value && ..., AllowCopy, DisallowCopy>::type where AllowCopy is empty and DisallowCopy has deleted copy operations (among other things). I also check for default c'tible allocator, and all of this is still not quite right, e.g. I support neither allocator propagation on copy nor copying of elements in the allocator.
    – Arne Vogel
    2 hours ago






  • 2




    is_nothrow_move_constructible_v<Node> is false in MSVC, so it tries to copy and fails; it is true in gcc, so it moves. If is_nothrow_move_constructible_v<Node> is forced to become false in gcc, it also tries to copy and fails. Why do MSVC and gcc disagree on is_nothrow_move_constructible_v<Node> value?
    – Evg
    1 hour ago














up vote
5
down vote













When you declare a move constructor, the implicitly declared copy constructor is defined as deleted. On the other hand, when you don't declare a move constructor, the compiler implicitly defines the copy constructor when it need it. And this implicit definition is ill-formed.



unique_ptr is not CopyInsertable in a container that uses a standard allocator because it is not copy constructible so the copy constructor of map_ is ill-formed (it could have been declared as deleted, but this is not required by the standard).



As your example code show us, with newer version of MSVC, this ill-formed definition is generated with this example code. I do not think there is something in the standard that forbids it (even if this is realy surprising).



So you should indeed ensure that the copy constructor of Node is declared or implicitly defined as deleted.






share|improve this answer


















  • 1




    Im wondering as to why the issue is not seen with clang or gcc ?
    – darune
    4 hours ago






  • 1




    @darune I think the bug is in the standard requirement. If a standard type is not copy constructible it should be possible to check it with traits. But actually implementing such a requirement would be a real pain. I hope that with concepts this will be fixed.
    – Oliv
    3 hours ago






  • 1




    I don't have VS2017 accessible, but couldn't be the problem with that implicitly-declared move constructor of std::unordered_map (and therefore of Node) is not noexcept? Then, push_back, i.e., emplace_back, may prefer copy constructor (during reallocation) than potentially throwing move constructor (std::move_if_noexcept)? With user-declared move constructor, the copy constructor is deleted, so throwing move constructor will be used.
    – Daniel Langr
    3 hours ago







  • 3




    The type traits magic to do this "correctly" is surprisingly complex. I implemented an open addressing hash map (and set) that is mostly API-compatible with unordered_map, and ended up having to use private inheritance from std::conditional<std::is_copy_constructible<...>::value && ..., AllowCopy, DisallowCopy>::type where AllowCopy is empty and DisallowCopy has deleted copy operations (among other things). I also check for default c'tible allocator, and all of this is still not quite right, e.g. I support neither allocator propagation on copy nor copying of elements in the allocator.
    – Arne Vogel
    2 hours ago






  • 2




    is_nothrow_move_constructible_v<Node> is false in MSVC, so it tries to copy and fails; it is true in gcc, so it moves. If is_nothrow_move_constructible_v<Node> is forced to become false in gcc, it also tries to copy and fails. Why do MSVC and gcc disagree on is_nothrow_move_constructible_v<Node> value?
    – Evg
    1 hour ago












up vote
5
down vote










up vote
5
down vote









When you declare a move constructor, the implicitly declared copy constructor is defined as deleted. On the other hand, when you don't declare a move constructor, the compiler implicitly defines the copy constructor when it need it. And this implicit definition is ill-formed.



unique_ptr is not CopyInsertable in a container that uses a standard allocator because it is not copy constructible so the copy constructor of map_ is ill-formed (it could have been declared as deleted, but this is not required by the standard).



As your example code show us, with newer version of MSVC, this ill-formed definition is generated with this example code. I do not think there is something in the standard that forbids it (even if this is realy surprising).



So you should indeed ensure that the copy constructor of Node is declared or implicitly defined as deleted.






share|improve this answer














When you declare a move constructor, the implicitly declared copy constructor is defined as deleted. On the other hand, when you don't declare a move constructor, the compiler implicitly defines the copy constructor when it need it. And this implicit definition is ill-formed.



unique_ptr is not CopyInsertable in a container that uses a standard allocator because it is not copy constructible so the copy constructor of map_ is ill-formed (it could have been declared as deleted, but this is not required by the standard).



As your example code show us, with newer version of MSVC, this ill-formed definition is generated with this example code. I do not think there is something in the standard that forbids it (even if this is realy surprising).



So you should indeed ensure that the copy constructor of Node is declared or implicitly defined as deleted.







share|improve this answer














share|improve this answer



share|improve this answer








edited 3 hours ago

























answered 4 hours ago









Oliv

7,1381750




7,1381750







  • 1




    Im wondering as to why the issue is not seen with clang or gcc ?
    – darune
    4 hours ago






  • 1




    @darune I think the bug is in the standard requirement. If a standard type is not copy constructible it should be possible to check it with traits. But actually implementing such a requirement would be a real pain. I hope that with concepts this will be fixed.
    – Oliv
    3 hours ago






  • 1




    I don't have VS2017 accessible, but couldn't be the problem with that implicitly-declared move constructor of std::unordered_map (and therefore of Node) is not noexcept? Then, push_back, i.e., emplace_back, may prefer copy constructor (during reallocation) than potentially throwing move constructor (std::move_if_noexcept)? With user-declared move constructor, the copy constructor is deleted, so throwing move constructor will be used.
    – Daniel Langr
    3 hours ago







  • 3




    The type traits magic to do this "correctly" is surprisingly complex. I implemented an open addressing hash map (and set) that is mostly API-compatible with unordered_map, and ended up having to use private inheritance from std::conditional<std::is_copy_constructible<...>::value && ..., AllowCopy, DisallowCopy>::type where AllowCopy is empty and DisallowCopy has deleted copy operations (among other things). I also check for default c'tible allocator, and all of this is still not quite right, e.g. I support neither allocator propagation on copy nor copying of elements in the allocator.
    – Arne Vogel
    2 hours ago






  • 2




    is_nothrow_move_constructible_v<Node> is false in MSVC, so it tries to copy and fails; it is true in gcc, so it moves. If is_nothrow_move_constructible_v<Node> is forced to become false in gcc, it also tries to copy and fails. Why do MSVC and gcc disagree on is_nothrow_move_constructible_v<Node> value?
    – Evg
    1 hour ago












  • 1




    Im wondering as to why the issue is not seen with clang or gcc ?
    – darune
    4 hours ago






  • 1




    @darune I think the bug is in the standard requirement. If a standard type is not copy constructible it should be possible to check it with traits. But actually implementing such a requirement would be a real pain. I hope that with concepts this will be fixed.
    – Oliv
    3 hours ago






  • 1




    I don't have VS2017 accessible, but couldn't be the problem with that implicitly-declared move constructor of std::unordered_map (and therefore of Node) is not noexcept? Then, push_back, i.e., emplace_back, may prefer copy constructor (during reallocation) than potentially throwing move constructor (std::move_if_noexcept)? With user-declared move constructor, the copy constructor is deleted, so throwing move constructor will be used.
    – Daniel Langr
    3 hours ago







  • 3




    The type traits magic to do this "correctly" is surprisingly complex. I implemented an open addressing hash map (and set) that is mostly API-compatible with unordered_map, and ended up having to use private inheritance from std::conditional<std::is_copy_constructible<...>::value && ..., AllowCopy, DisallowCopy>::type where AllowCopy is empty and DisallowCopy has deleted copy operations (among other things). I also check for default c'tible allocator, and all of this is still not quite right, e.g. I support neither allocator propagation on copy nor copying of elements in the allocator.
    – Arne Vogel
    2 hours ago






  • 2




    is_nothrow_move_constructible_v<Node> is false in MSVC, so it tries to copy and fails; it is true in gcc, so it moves. If is_nothrow_move_constructible_v<Node> is forced to become false in gcc, it also tries to copy and fails. Why do MSVC and gcc disagree on is_nothrow_move_constructible_v<Node> value?
    – Evg
    1 hour ago







1




1




Im wondering as to why the issue is not seen with clang or gcc ?
– darune
4 hours ago




Im wondering as to why the issue is not seen with clang or gcc ?
– darune
4 hours ago




1




1




@darune I think the bug is in the standard requirement. If a standard type is not copy constructible it should be possible to check it with traits. But actually implementing such a requirement would be a real pain. I hope that with concepts this will be fixed.
– Oliv
3 hours ago




@darune I think the bug is in the standard requirement. If a standard type is not copy constructible it should be possible to check it with traits. But actually implementing such a requirement would be a real pain. I hope that with concepts this will be fixed.
– Oliv
3 hours ago




1




1




I don't have VS2017 accessible, but couldn't be the problem with that implicitly-declared move constructor of std::unordered_map (and therefore of Node) is not noexcept? Then, push_back, i.e., emplace_back, may prefer copy constructor (during reallocation) than potentially throwing move constructor (std::move_if_noexcept)? With user-declared move constructor, the copy constructor is deleted, so throwing move constructor will be used.
– Daniel Langr
3 hours ago





I don't have VS2017 accessible, but couldn't be the problem with that implicitly-declared move constructor of std::unordered_map (and therefore of Node) is not noexcept? Then, push_back, i.e., emplace_back, may prefer copy constructor (during reallocation) than potentially throwing move constructor (std::move_if_noexcept)? With user-declared move constructor, the copy constructor is deleted, so throwing move constructor will be used.
– Daniel Langr
3 hours ago





3




3




The type traits magic to do this "correctly" is surprisingly complex. I implemented an open addressing hash map (and set) that is mostly API-compatible with unordered_map, and ended up having to use private inheritance from std::conditional<std::is_copy_constructible<...>::value && ..., AllowCopy, DisallowCopy>::type where AllowCopy is empty and DisallowCopy has deleted copy operations (among other things). I also check for default c'tible allocator, and all of this is still not quite right, e.g. I support neither allocator propagation on copy nor copying of elements in the allocator.
– Arne Vogel
2 hours ago




The type traits magic to do this "correctly" is surprisingly complex. I implemented an open addressing hash map (and set) that is mostly API-compatible with unordered_map, and ended up having to use private inheritance from std::conditional<std::is_copy_constructible<...>::value && ..., AllowCopy, DisallowCopy>::type where AllowCopy is empty and DisallowCopy has deleted copy operations (among other things). I also check for default c'tible allocator, and all of this is still not quite right, e.g. I support neither allocator propagation on copy nor copying of elements in the allocator.
– Arne Vogel
2 hours ago




2




2




is_nothrow_move_constructible_v<Node> is false in MSVC, so it tries to copy and fails; it is true in gcc, so it moves. If is_nothrow_move_constructible_v<Node> is forced to become false in gcc, it also tries to copy and fails. Why do MSVC and gcc disagree on is_nothrow_move_constructible_v<Node> value?
– Evg
1 hour ago




is_nothrow_move_constructible_v<Node> is false in MSVC, so it tries to copy and fails; it is true in gcc, so it moves. If is_nothrow_move_constructible_v<Node> is forced to become false in gcc, it also tries to copy and fails. Why do MSVC and gcc disagree on is_nothrow_move_constructible_v<Node> value?
– Evg
1 hour ago










up vote
3
down vote













Let's look at the std::vector source code (I replaced pointer and _Ty with actual types):



void _Umove_if_noexcept1(Node* First, Node* Last, Node* Dest, true_type)
// move [First, Last) to raw Dest, using allocator
_Uninitialized_move(First, Last, Dest, this->_Getal());


void _Umove_if_noexcept1(Node* First, Node* Last, Node* Dest, false_type)
// copy [First, Last) to raw Dest, using allocator
_Uninitialized_copy(First, Last, Dest, this->_Getal());


void _Umove_if_noexcept(Node* First, Node* Last, Node* Dest)
// move_if_noexcept [First, Last) to raw Dest, using allocator
_Umove_if_noexcept1(First, Last, Dest,
bool_constant<disjunction_v<is_nothrow_move_constructible<Node>, negation<is_copy_constructible<Node>>>>);



If Node is no-throw move-constructible or is not copy-constructible, _Uninitialized_move is called, otherwise, _Uninitialized_copy is called.



The problem is that the type trait std::is_copy_constructible_v is true for Node if you do not declare a move constructor explicitly. This declaration makes copy-constructor deleted.



libstdc++ implements std::vector in a similar way, but there std::is_nothrow_move_constructible_v<Node> is true in contrast to MSVC, where it is false. So, move semantics is used and the compiler does not try to generate the copy-constructor.



But if we force is_nothrow_move_constructible_v to become false



struct Base 
Base() = default;
Base(const S&) = default;
Base(Base&&) noexcept(false)
;

struct Node : Base
std::unordered_map<int, std::unique_ptr<int>> map;
;

int main()
std::vector<Node> vec;
vec.reserve(1);



the same error occurs:



/usr/include/c++/7/ext/new_allocator.h:136:4: error: use of deleted function ‘std::pair<_T1, _T2>::pair(const std::pair<_T1, _T2>&) [with _T1 = const int; _T2 = std::unique_ptr<int>]’
::new((void *)__p) _Up(std::forward<_Args>(__args)...);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~





share|improve this answer


















  • 1




    Im wondering as to why the issue is not seen with clang or gcc ?
    – darune
    4 hours ago






  • 1




    @darune, the dumb answer is because gcc's and clang's implementations do not rely on std::is_copy_constructible in this particular piece of code. I don't have a better answer now.
    – Evg
    3 hours ago










  • If i understand you correctly that code is in the MSVC vector implementation (?) and isn't that a bug in this case ? It does feel like like a bug, because invoking the move constructor directly does compile fine.
    – darune
    3 hours ago











  • @darune, yes, the code in my answer is directly from MSVC STL implementation. I agree that it feels like a bug.
    – Evg
    3 hours ago










  • @darune, added some info about in libstdc++.
    – Evg
    2 hours ago














up vote
3
down vote













Let's look at the std::vector source code (I replaced pointer and _Ty with actual types):



void _Umove_if_noexcept1(Node* First, Node* Last, Node* Dest, true_type)
// move [First, Last) to raw Dest, using allocator
_Uninitialized_move(First, Last, Dest, this->_Getal());


void _Umove_if_noexcept1(Node* First, Node* Last, Node* Dest, false_type)
// copy [First, Last) to raw Dest, using allocator
_Uninitialized_copy(First, Last, Dest, this->_Getal());


void _Umove_if_noexcept(Node* First, Node* Last, Node* Dest)
// move_if_noexcept [First, Last) to raw Dest, using allocator
_Umove_if_noexcept1(First, Last, Dest,
bool_constant<disjunction_v<is_nothrow_move_constructible<Node>, negation<is_copy_constructible<Node>>>>);



If Node is no-throw move-constructible or is not copy-constructible, _Uninitialized_move is called, otherwise, _Uninitialized_copy is called.



The problem is that the type trait std::is_copy_constructible_v is true for Node if you do not declare a move constructor explicitly. This declaration makes copy-constructor deleted.



libstdc++ implements std::vector in a similar way, but there std::is_nothrow_move_constructible_v<Node> is true in contrast to MSVC, where it is false. So, move semantics is used and the compiler does not try to generate the copy-constructor.



But if we force is_nothrow_move_constructible_v to become false



struct Base 
Base() = default;
Base(const S&) = default;
Base(Base&&) noexcept(false)
;

struct Node : Base
std::unordered_map<int, std::unique_ptr<int>> map;
;

int main()
std::vector<Node> vec;
vec.reserve(1);



the same error occurs:



/usr/include/c++/7/ext/new_allocator.h:136:4: error: use of deleted function ‘std::pair<_T1, _T2>::pair(const std::pair<_T1, _T2>&) [with _T1 = const int; _T2 = std::unique_ptr<int>]’
::new((void *)__p) _Up(std::forward<_Args>(__args)...);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~





share|improve this answer


















  • 1




    Im wondering as to why the issue is not seen with clang or gcc ?
    – darune
    4 hours ago






  • 1




    @darune, the dumb answer is because gcc's and clang's implementations do not rely on std::is_copy_constructible in this particular piece of code. I don't have a better answer now.
    – Evg
    3 hours ago










  • If i understand you correctly that code is in the MSVC vector implementation (?) and isn't that a bug in this case ? It does feel like like a bug, because invoking the move constructor directly does compile fine.
    – darune
    3 hours ago











  • @darune, yes, the code in my answer is directly from MSVC STL implementation. I agree that it feels like a bug.
    – Evg
    3 hours ago










  • @darune, added some info about in libstdc++.
    – Evg
    2 hours ago












up vote
3
down vote










up vote
3
down vote









Let's look at the std::vector source code (I replaced pointer and _Ty with actual types):



void _Umove_if_noexcept1(Node* First, Node* Last, Node* Dest, true_type)
// move [First, Last) to raw Dest, using allocator
_Uninitialized_move(First, Last, Dest, this->_Getal());


void _Umove_if_noexcept1(Node* First, Node* Last, Node* Dest, false_type)
// copy [First, Last) to raw Dest, using allocator
_Uninitialized_copy(First, Last, Dest, this->_Getal());


void _Umove_if_noexcept(Node* First, Node* Last, Node* Dest)
// move_if_noexcept [First, Last) to raw Dest, using allocator
_Umove_if_noexcept1(First, Last, Dest,
bool_constant<disjunction_v<is_nothrow_move_constructible<Node>, negation<is_copy_constructible<Node>>>>);



If Node is no-throw move-constructible or is not copy-constructible, _Uninitialized_move is called, otherwise, _Uninitialized_copy is called.



The problem is that the type trait std::is_copy_constructible_v is true for Node if you do not declare a move constructor explicitly. This declaration makes copy-constructor deleted.



libstdc++ implements std::vector in a similar way, but there std::is_nothrow_move_constructible_v<Node> is true in contrast to MSVC, where it is false. So, move semantics is used and the compiler does not try to generate the copy-constructor.



But if we force is_nothrow_move_constructible_v to become false



struct Base 
Base() = default;
Base(const S&) = default;
Base(Base&&) noexcept(false)
;

struct Node : Base
std::unordered_map<int, std::unique_ptr<int>> map;
;

int main()
std::vector<Node> vec;
vec.reserve(1);



the same error occurs:



/usr/include/c++/7/ext/new_allocator.h:136:4: error: use of deleted function ‘std::pair<_T1, _T2>::pair(const std::pair<_T1, _T2>&) [with _T1 = const int; _T2 = std::unique_ptr<int>]’
::new((void *)__p) _Up(std::forward<_Args>(__args)...);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~





share|improve this answer














Let's look at the std::vector source code (I replaced pointer and _Ty with actual types):



void _Umove_if_noexcept1(Node* First, Node* Last, Node* Dest, true_type)
// move [First, Last) to raw Dest, using allocator
_Uninitialized_move(First, Last, Dest, this->_Getal());


void _Umove_if_noexcept1(Node* First, Node* Last, Node* Dest, false_type)
// copy [First, Last) to raw Dest, using allocator
_Uninitialized_copy(First, Last, Dest, this->_Getal());


void _Umove_if_noexcept(Node* First, Node* Last, Node* Dest)
// move_if_noexcept [First, Last) to raw Dest, using allocator
_Umove_if_noexcept1(First, Last, Dest,
bool_constant<disjunction_v<is_nothrow_move_constructible<Node>, negation<is_copy_constructible<Node>>>>);



If Node is no-throw move-constructible or is not copy-constructible, _Uninitialized_move is called, otherwise, _Uninitialized_copy is called.



The problem is that the type trait std::is_copy_constructible_v is true for Node if you do not declare a move constructor explicitly. This declaration makes copy-constructor deleted.



libstdc++ implements std::vector in a similar way, but there std::is_nothrow_move_constructible_v<Node> is true in contrast to MSVC, where it is false. So, move semantics is used and the compiler does not try to generate the copy-constructor.



But if we force is_nothrow_move_constructible_v to become false



struct Base 
Base() = default;
Base(const S&) = default;
Base(Base&&) noexcept(false)
;

struct Node : Base
std::unordered_map<int, std::unique_ptr<int>> map;
;

int main()
std::vector<Node> vec;
vec.reserve(1);



the same error occurs:



/usr/include/c++/7/ext/new_allocator.h:136:4: error: use of deleted function ‘std::pair<_T1, _T2>::pair(const std::pair<_T1, _T2>&) [with _T1 = const int; _T2 = std::unique_ptr<int>]’
::new((void *)__p) _Up(std::forward<_Args>(__args)...);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~






share|improve this answer














share|improve this answer



share|improve this answer








edited 2 hours ago

























answered 4 hours ago









Evg

3,42811334




3,42811334







  • 1




    Im wondering as to why the issue is not seen with clang or gcc ?
    – darune
    4 hours ago






  • 1




    @darune, the dumb answer is because gcc's and clang's implementations do not rely on std::is_copy_constructible in this particular piece of code. I don't have a better answer now.
    – Evg
    3 hours ago










  • If i understand you correctly that code is in the MSVC vector implementation (?) and isn't that a bug in this case ? It does feel like like a bug, because invoking the move constructor directly does compile fine.
    – darune
    3 hours ago











  • @darune, yes, the code in my answer is directly from MSVC STL implementation. I agree that it feels like a bug.
    – Evg
    3 hours ago










  • @darune, added some info about in libstdc++.
    – Evg
    2 hours ago












  • 1




    Im wondering as to why the issue is not seen with clang or gcc ?
    – darune
    4 hours ago






  • 1




    @darune, the dumb answer is because gcc's and clang's implementations do not rely on std::is_copy_constructible in this particular piece of code. I don't have a better answer now.
    – Evg
    3 hours ago










  • If i understand you correctly that code is in the MSVC vector implementation (?) and isn't that a bug in this case ? It does feel like like a bug, because invoking the move constructor directly does compile fine.
    – darune
    3 hours ago











  • @darune, yes, the code in my answer is directly from MSVC STL implementation. I agree that it feels like a bug.
    – Evg
    3 hours ago










  • @darune, added some info about in libstdc++.
    – Evg
    2 hours ago







1




1




Im wondering as to why the issue is not seen with clang or gcc ?
– darune
4 hours ago




Im wondering as to why the issue is not seen with clang or gcc ?
– darune
4 hours ago




1




1




@darune, the dumb answer is because gcc's and clang's implementations do not rely on std::is_copy_constructible in this particular piece of code. I don't have a better answer now.
– Evg
3 hours ago




@darune, the dumb answer is because gcc's and clang's implementations do not rely on std::is_copy_constructible in this particular piece of code. I don't have a better answer now.
– Evg
3 hours ago












If i understand you correctly that code is in the MSVC vector implementation (?) and isn't that a bug in this case ? It does feel like like a bug, because invoking the move constructor directly does compile fine.
– darune
3 hours ago





If i understand you correctly that code is in the MSVC vector implementation (?) and isn't that a bug in this case ? It does feel like like a bug, because invoking the move constructor directly does compile fine.
– darune
3 hours ago













@darune, yes, the code in my answer is directly from MSVC STL implementation. I agree that it feels like a bug.
– Evg
3 hours ago




@darune, yes, the code in my answer is directly from MSVC STL implementation. I agree that it feels like a bug.
– Evg
3 hours ago












@darune, added some info about in libstdc++.
– Evg
2 hours ago




@darune, added some info about in libstdc++.
– Evg
2 hours ago










finn is a new contributor. Be nice, and check out our Code of Conduct.









 

draft saved


draft discarded


















finn is a new contributor. Be nice, and check out our Code of Conduct.












finn is a new contributor. Be nice, and check out our Code of Conduct.











finn is a new contributor. Be nice, and check out our Code of Conduct.













 


draft saved


draft discarded














StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53168836%2fdoes-visual-studio-2017-need-an-explicit-move-constructor-declaration%23new-answer', 'question_page');

);

Post as a guest













































































Comments

Popular posts from this blog

What does second last employer means? [closed]

List of Gilmore Girls characters

Confectionery