Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Copy assign a map if element is not assignable

Tags:

c++

struct const_int { const int x = 1; };

int main(int argc, char **argv)
{
    std::unordered_map<int, const_int> map0;
    std::unordered_map<int, const_int> map1 { map0 }; // OK
    map1 = map0; // Compile-time error
    return 0;
}

This code works in Visual C++ 2017, but fails with compile error in Visual C++ 2019:

14.23.27911\include\list(1210): error C2280: 'std::pair<_Kty,_Ty> &std::pair<_Kty,_Ty>::operator =(volatile const std::pair<_Kty,_Ty> &)': attempting to reference a deleted function
            with
            [
                _Kty=int,
                _Ty=const_int
            ]

Which compiler version has correct implementation, or behavior is not defined in this case?

like image 674
random Avatar asked Aug 08 '19 06:08

random


2 Answers

Quoting Billy Robert O'Neil III from visual studio developer community this is not a bug:

This is not a bug. unordered_map is an allocator-aware container, and the assignment operation of allocator-aware containers requires that the value_type be copy assignable - see containers.allocatoraware - and then unord.req says that for the purposes of requirements in the unordered containers, one looks to key_type and mapped_type instead.

This happened to work in previous releases of Visual C++ because std::list, out of which std::unordered_map is constructed, used to deallocate all nodes during an assignment operation, so even though it was permitted to assign elements, we happened not to. But that meant that assigning 100 values into a list already containing 100 elements would do 200 unnecessary calls to the allocator: deallocating all 100 old nodes, then allocating 100 new nodes. The behavior change you observe is because in VS2019 Update 2, we implemented an optimization to reuse already-allocated nodes in an assignment.

like image 199
MofX Avatar answered Oct 06 '22 20:10

MofX


In addition to @MofX's answer I'd like to add some resources here, also because the quoted text contains invalid links.

From [unord.map]/2 (emphasis mine):

An unordered_­map satisfies all of the requirements of a container, of an unordered associative container, and of an allocator-aware container.

This leads to [container.requirements.general]/16, where for the assignment expression in Table 86 the requirements are (emphasis mine):

Requires: T is CopyInsertable into X and CopyAssignable.

Of course, the type used in the OP's example struct const_int { const int x = 1; }; is not copy assignable (due to the const and no user-defined assignment operator) and therefore the compilation fails.

I hope this makes it clearer.

(Disclaimer: Initially I was convinced MSVC has a bug here but I was proven wrong)

like image 45
andreee Avatar answered Oct 06 '22 22:10

andreee