does vs2017 need explicit move constructor declaration

  • A+

The below code can be compiled successfully using vs2015, but failed using vs2017. vs2017 report "error C2280: “std::pair<const _Kty,_Ty>::pair(const std::pair<const _Kty,_Ty> &)”: attempting to reference a deleted function".

#include <unordered_map> #include <memory>  struct Node {   std::unordered_map<int, std::unique_ptr<int>> map_;   //uncomment the following two lines will pass vs2017 compile   //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 vs2017 need explicit move constructor declaration. could anybody explain the reason? thanks.


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:

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 


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.


:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: