Копировать назначить карту, если элемент не может быть назначен
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;
}
Этот код работает в Visual C++ 2017, но завершается ошибкой с ошибкой компиляции в 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
]
Какая версия компилятора имеет правильную реализацию или поведение в этом случае не определено?
Ответы
Ответ 1
Цитируя Билли Роберта О'Нила III из сообщества разработчиков Visual Studio, это не ошибка:
Это не ошибка. unordered_map
является контейнером с распределителем, и операция назначения контейнеров с распределителем требует, чтобы value_type
был назначаемым для копирования - см. containers.allocatoraware
- а затем unord.req
говорит, что для целей требований к неупорядоченным контейнерам вместо этого обращаются к key_type
и mapped_type
.
Это сработало в предыдущих выпусках Visual C++, потому что std::list
, из которого построен std::unordered_map
, использовался для освобождения всех узлов во время операции присваивания, поэтому, хотя было разрешено присваивать элементы, мы случайно не, Но это означало, что назначение 100 значений в список, уже содержащий 100 элементов, будет делать 200 ненужных вызовов для распределителя: освобождение всех 100 старых узлов, затем выделение 100 новых узлов. Изменение поведения, которое вы наблюдаете, связано с тем, что в VS2019 Update 2 мы реализовали оптимизацию для повторного использования уже выделенных узлов в назначении.
Ответ 2
В дополнение к ответу @MofX я хотел бы добавить сюда несколько ресурсов, в том числе и потому, что цитируемый текст содержит недействительные ссылки.
С [unord.map]/2 (выделено мной):
unordered_map
удовлетворяет всем требованиям контейнера, неупорядоченный ассоциативный контейнер и с учетом распределителя контейнер.
Это приводит к [container.requirements.general]/16, где для выражения присваивания в Таблице 86 требования (выделены мной):
Требуется: T является CopyInsertable для X и CopyAssignable.
Разумеется, тип, используемый в примере OP struct const_int { const int x = 1; };
, не может быть назначен для копирования (из-за const
и без пользовательского оператора присваивания), и поэтому компиляция не удалась.
Надеюсь, это прояснит ситуацию.
(Отказ от ответственности: Первоначально я был убежден, что MSVC имеет ошибку здесь, но я ошибся)