3

I have a vector of map. The maps contain a struct that is move-only.

When I try to interact with this structure, I get compilation errors on MSVC.

Here's a sample code:

#include <map>
#include <memory>
#include <vector>

struct S {
    std::unique_ptr<int> a;
};

int main() {
    std::vector<std::map<float, S>> s;
    
    s.emplace_back();

    return 0;
}

I get an error on the s.emplace_back() line:

C:/data/msvc/14.28.29333/include\xmemory(701): error C2280: 'std::pair<const float,S>::pair(const std::pair<const float,S> &)': attempting to reference a deleted function
C:/data/msvc/14.28.29333/include\utility(209): note: see declaration of 'std::pair<const float,S>::pair'
C:/data/msvc/14.28.29333/include\utility(209): note: 'std::pair<const float,S>::pair(const std::pair<const float,S> &)': function was implicitly deleted because a data member invokes a deleted or inaccessible function 'S::S(const S &)'
<source>(7): note: 'S::S(const S &)': function was implicitly deleted because a data member invokes a deleted or inaccessible function 'std::unique_ptr<int,std::default_delete<int>>::unique_ptr(const std::unique_ptr<int,std::default_delete<int>> &)'
C:/data/msvc/14.28.29333/include\memory(2686): note: 'std::unique_ptr<int,std::default_delete<int>>::unique_ptr(const std::unique_ptr<int,std::default_delete<int>> &)': function was explicitly deleted
C:/data/msvc/14.28.29333/include\xtree(358): note: see reference to function template instantiation 'void std::_Default_allocator_traits<_Alloc>::construct<_Ty,_Ty&>(_Alloc &,_Objty *const ,_Ty &)' being compiled
        with
        [
            _Alloc=std::allocator<std::_Tree_node<std::pair<const float,S>,std::_Default_allocator_traits<std::allocator<std::pair<const float,S>>>::void_pointer>>,
            _Ty=std::pair<const float,S>,
            _Objty=std::pair<const float,S>
        ]
C:/data/msvc/14.28.29333/include\xtree(358): note: see reference to function template instantiation 'void std::_Default_allocator_traits<_Alloc>::construct<_Ty,_Ty&>(_Alloc &,_Objty *const ,_Ty &)' being compiled
        with
        [
            _Alloc=std::allocator<std::_Tree_node<std::pair<const float,S>,std::_Default_allocator_traits<std::allocator<std::pair<const float,S>>>::void_pointer>>,
            _Ty=std::pair<const float,S>,
            _Objty=std::pair<const float,S>
        ]
C:/data/msvc/14.28.29333/include\xtree(2027): note: see reference to function template instantiation 'std::_Tree_node<std::pair<const float,S>,std::_Default_allocator_traits<_Alloc>::void_pointer> *std::_Tree_node<std::pair<const float,S>,std::_Default_allocator_traits<_Alloc>::void_pointer>::_Buynode<std::allocator<std::_Tree_node<std::pair<const float,S>,std::_Default_allocator_traits<_Alloc>::void_pointer>>,_Ty&>(std::allocator<std::_Tree_node<std::pair<const float,S>,std::_Default_allocator_traits<_Alloc>::void_pointer>> &,std::_Tree_node<std::pair<const float,S>,std::_Default_allocator_traits<_Alloc>::void_pointer> *,_Ty &)' being compiled
        with
        [
            _Alloc=std::allocator<std::pair<const float,S>>,
            _Ty=std::pair<const float,S>
        ]
C:/data/msvc/14.28.29333/include\xtree(2027): note: see reference to function template instantiation 'std::_Tree_node<std::pair<const float,S>,std::_Default_allocator_traits<_Alloc>::void_pointer> *std::_Tree_node<std::pair<const float,S>,std::_Default_allocator_traits<_Alloc>::void_pointer>::_Buynode<std::allocator<std::_Tree_node<std::pair<const float,S>,std::_Default_allocator_traits<_Alloc>::void_pointer>>,_Ty&>(std::allocator<std::_Tree_node<std::pair<const float,S>,std::_Default_allocator_traits<_Alloc>::void_pointer>> &,std::_Tree_node<std::pair<const float,S>,std::_Default_allocator_traits<_Alloc>::void_pointer> *,_Ty &)' being compiled
        with
        [
            _Alloc=std::allocator<std::pair<const float,S>>,
            _Ty=std::pair<const float,S>
        ]
C:/data/msvc/14.28.29333/include\xtree(1741): note: see reference to function template instantiation 'std::_Tree_node<std::pair<const float,S>,std::_Default_allocator_traits<_Alloc>::void_pointer> *std::_Tree<std::_Tmap_traits<_Kty,_Ty,_Pr,_Alloc,false>>::_Buynode<std::pair<const float,S>&>(std::pair<const float,S> &)' being compiled
        with
        [
            _Alloc=std::allocator<std::pair<const float,S>>,
            _Kty=float,
            _Ty=S,
            _Pr=std::less<float>
        ]
C:/data/msvc/14.28.29333/include\xtree(1741): note: see reference to function template instantiation 'std::_Tree_node<std::pair<const float,S>,std::_Default_allocator_traits<_Alloc>::void_pointer> *std::_Tree<std::_Tmap_traits<_Kty,_Ty,_Pr,_Alloc,false>>::_Buynode<std::pair<const float,S>&>(std::pair<const float,S> &)' being compiled
        with
        [
            _Alloc=std::allocator<std::pair<const float,S>>,
            _Kty=float,
            _Ty=S,
            _Pr=std::less<float>
        ]
C:/data/msvc/14.28.29333/include\xtree(1762): note: see reference to function template instantiation 'std::_Tree_node<std::pair<const float,S>,std::_Default_allocator_traits<_Alloc>::void_pointer> *std::_Tree<std::_Tmap_traits<_Kty,_Ty,_Pr,_Alloc,false>>::_Copy_or_move<std::pair<const float,S>,std::integral_constant<bool,false>>(std::pair<const float,S> &,std::_Tree<std::_Tmap_traits<_Kty,_Ty,_Pr,_Alloc,false>>::_Copy_tag,_Is_set)' being compiled
        with
        [
            _Alloc=std::allocator<std::pair<const float,S>>,
            _Kty=float,
            _Ty=S,
            _Pr=std::less<float>,
            _Is_set=std::integral_constant<bool,false>
        ]
C:/data/msvc/14.28.29333/include\xtree(1762): note: see reference to function template instantiation 'std::_Tree_node<std::pair<const float,S>,std::_Default_allocator_traits<_Alloc>::void_pointer> *std::_Tree<std::_Tmap_traits<_Kty,_Ty,_Pr,_Alloc,false>>::_Copy_or_move<std::pair<const float,S>,std::integral_constant<bool,false>>(std::pair<const float,S> &,std::_Tree<std::_Tmap_traits<_Kty,_Ty,_Pr,_Alloc,false>>::_Copy_tag,_Is_set)' being compiled
        with
        [
            _Alloc=std::allocator<std::pair<const float,S>>,
            _Kty=float,
            _Ty=S,
            _Pr=std::less<float>,
            _Is_set=std::integral_constant<bool,false>
        ]
C:/data/msvc/14.28.29333/include\xtree(1728): note: see reference to function template instantiation 'std::_Tree_node<std::pair<const float,S>,std::_Default_allocator_traits<_Alloc>::void_pointer> *std::_Tree<std::_Tmap_traits<_Kty,_Ty,_Pr,_Alloc,false>>::_Copy_nodes<_Moveit>(std::_Tree_node<std::pair<const float,S>,std::_Default_allocator_traits<_Alloc>::void_pointer> *,std::_Tree_node<std::pair<const float,S>,std::_Default_allocator_traits<_Alloc>::void_pointer> *,_Moveit)' being compiled
        with
        [
            _Alloc=std::allocator<std::pair<const float,S>>,
            _Kty=float,
            _Ty=S,
            _Pr=std::less<float>,
            _Moveit=std::_Tree<std::_Tmap_traits<float,S,std::less<float>,std::allocator<std::pair<const float,S>>,false>>::_Copy_tag
        ]
C:/data/msvc/14.28.29333/include\xtree(1728): note: see reference to function template instantiation 'std::_Tree_node<std::pair<const float,S>,std::_Default_allocator_traits<_Alloc>::void_pointer> *std::_Tree<std::_Tmap_traits<_Kty,_Ty,_Pr,_Alloc,false>>::_Copy_nodes<_Moveit>(std::_Tree_node<std::pair<const float,S>,std::_Default_allocator_traits<_Alloc>::void_pointer> *,std::_Tree_node<std::pair<const float,S>,std::_Default_allocator_traits<_Alloc>::void_pointer> *,_Moveit)' being compiled
        with
        [
            _Alloc=std::allocator<std::pair<const float,S>>,
            _Kty=float,
            _Ty=S,
            _Pr=std::less<float>,
            _Moveit=std::_Tree<std::_Tmap_traits<float,S,std::less<float>,std::allocator<std::pair<const float,S>>,false>>::_Copy_tag
        ]
C:/data/msvc/14.28.29333/include\xtree(902): note: see reference to function template instantiation 'void std::_Tree<std::_Tmap_traits<_Kty,_Ty,_Pr,_Alloc,false>>::_Copy<std::_Tree<std::_Tmap_traits<_Kty,_Ty,_Pr,_Alloc,false>>::_Copy_tag>(const std::_Tree<std::_Tmap_traits<_Kty,_Ty,_Pr,_Alloc,false>> &,_Moveit)' being compiled
        with
        [
            _Kty=float,
            _Ty=S,
            _Pr=std::less<float>,
            _Alloc=std::allocator<std::pair<const float,S>>,
            _Moveit=std::_Tree<std::_Tmap_traits<float,S,std::less<float>,std::allocator<std::pair<const float,S>>,false>>::_Copy_tag
        ]
C:/data/msvc/14.28.29333/include\xtree(902): note: see reference to function template instantiation 'void std::_Tree<std::_Tmap_traits<_Kty,_Ty,_Pr,_Alloc,false>>::_Copy<std::_Tree<std::_Tmap_traits<_Kty,_Ty,_Pr,_Alloc,false>>::_Copy_tag>(const std::_Tree<std::_Tmap_traits<_Kty,_Ty,_Pr,_Alloc,false>> &,_Moveit)' being compiled
        with
        [
            _Kty=float,
            _Ty=S,
            _Pr=std::less<float>,
            _Alloc=std::allocator<std::pair<const float,S>>,
            _Moveit=std::_Tree<std::_Tmap_traits<float,S,std::less<float>,std::allocator<std::pair<const float,S>>,false>>::_Copy_tag
        ]
C:/data/msvc/14.28.29333/include\map(111): note: see reference to function template instantiation 'std::_Tree<std::_Tmap_traits<_Kty,_Ty,_Pr,_Alloc,false>>::_Tree<std::allocator<std::_Tree_node<std::pair<const float,S>,std::_Default_allocator_traits<_Alloc>::void_pointer>>>(const std::_Tree<std::_Tmap_traits<_Kty,_Ty,_Pr,_Alloc,false>> &,_Any_alloc &&)' being compiled
        with
        [
            _Kty=float,
            _Ty=S,
            _Pr=std::less<float>,
            _Alloc=std::allocator<std::pair<const float,S>>,
            _Any_alloc=std::allocator<std::_Tree_node<std::pair<const float,S>,std::_Default_allocator_traits<std::allocator<std::pair<const float,S>>>::void_pointer>>
        ]
C:/data/msvc/14.28.29333/include\map(111): note: see reference to function template instantiation 'std::_Tree<std::_Tmap_traits<_Kty,_Ty,_Pr,_Alloc,false>>::_Tree<std::allocator<std::_Tree_node<std::pair<const float,S>,std::_Default_allocator_traits<_Alloc>::void_pointer>>>(const std::_Tree<std::_Tmap_traits<_Kty,_Ty,_Pr,_Alloc,false>> &,_Any_alloc &&)' being compiled
        with
        [
            _Kty=float,
            _Ty=S,
            _Pr=std::less<float>,
            _Alloc=std::allocator<std::pair<const float,S>>,
            _Any_alloc=std::allocator<std::_Tree_node<std::pair<const float,S>,std::_Default_allocator_traits<std::allocator<std::pair<const float,S>>>::void_pointer>>
        ]
C:/data/msvc/14.28.29333/include\map(111): note: while compiling class template member function 'std::map<float,S,std::less<float>,std::allocator<std::pair<const float,S>>>::map(const std::map<float,S,std::less<float>,std::allocator<std::pair<const float,S>>> &)'
C:/data/msvc/14.28.29333/include\xmemory(701): note: see reference to function template instantiation 'std::map<float,S,std::less<float>,std::allocator<std::pair<const float,S>>>::map(const std::map<float,S,std::less<float>,std::allocator<std::pair<const float,S>>> &)' being compiled
C:/data/msvc/14.28.29333/include\vector(687): note: see reference to class template instantiation 'std::map<float,S,std::less<float>,std::allocator<std::pair<const float,S>>>' being compiled
C:/data/msvc/14.28.29333/include\vector(705): note: see reference to function template instantiation 'void std::vector<std::map<float,S,std::less<float>,std::allocator<std::pair<const float,S>>>,std::allocator<std::map<float,S,std::less<float>,std::allocator<std::pair<const float,S>>>>>::_Emplace_back_with_unused_capacity<>(void)' being compiled
<source>(12): note: see reference to function template instantiation 'void std::vector<std::map<float,S,std::less<float>,std::allocator<std::pair<const float,S>>>,std::allocator<std::map<float,S,std::less<float>,std::allocator<std::pair<const float,S>>>>>::emplace_back<>(void)' being compiled
Compiler returned: 2

You can check the code in action on godbolt here: https://godbolt.org/z/qvMPYdc5q

It works in clang and GCC but not in MSVC (latest and v19.28).

I saw somewhere that MSVC requires the ctor & dtor to be noexcept for the map to use the move constructor, but even when defining noexcept ctors & dtors for the S struct, I get the same error.

What is MSVC doing that requires a copy in this piece of code? How can I force the move here?

Ebatsin
  • 552
  • 5
  • 17
  • TL;DR, it's `std::map` in MSVC that doesn't have a noexcept move constructor, so the vector has to make copies to grow to meet exception guarantees. That's where the copy comes from. – NathanOliver Jul 26 '21 at 12:03
  • BTW, from https://en.cppreference.com/w/cpp/container/map/map and https://eel.is/c++draft/map I don't see a `noexcept` requirement. – Jarod42 Jul 26 '21 at 12:16
  • Compiler might add extra `noexcept` (https://eel.is/c++draft/requirements#res.on.exception.handling-5). all compiler are compliant... :/ – Jarod42 Jul 26 '21 at 12:43

0 Answers0