При запуске цикла for на основе диапазона на std::unordered_map оказывается, что тип переменной цикла не использует ссылочные типы:
std::unordered_map<int, int> map = { {0, 1}, {1, 2}, {2, 3} };
for(auto&[l, r] : map)
static_assert(std::is_same_v<decltype(r), int&>);
MSVC 2017, gcc 8.2 и clang 7.0.0 сообщают здесь об ошибке утверждения. Противопоставьте это std::vector, где утверждение не будет ошибочным, как и следовало ожидать:
std::vector<int> vec = { 1, 2, 3 };
for(auto& r : vec)
static_assert(std::is_same_v<decltype(r), int&>);
Однако как в MSVC 2017, так и в gcc 8.2 цикл, изменяющий локальную переменную r, будет иметь наблюдаемые побочные эффекты:
#include <iostream>
#include <type_traits>
#include <unordered_map>
#include <vector>
int main() {
std::unordered_map<int, int> a = { {0, 1}, {1, 2}, {2, 3} };
for(auto[l, r] : a)
std::cout << l << "; " << r << std::endl;
for(auto&[l, r] : a) {
static_assert(std::is_same_v<decltype(r), int>);
r++;
}
std::cout << "Increment:" << std::endl;
for(auto[l, r] : a)
std::cout << l << "; " << r << std::endl;
}
Эта программа, например, будет печатать (игнорируя порядок):
0; 1
1; 2
2; 3
Increment:
0; 2
1; 3
2; 4
Что мне не хватает? Как это может изменить значение на карте, несмотря на то, что локальная переменная не имеет ссылочного типа? Или, что более уместно, почему std::is_same не видит правильный тип, потому что совершенно очевидно, что это ссылочный тип? Или мне не хватает какого-то неопределенного поведения?
Обратите внимание, что я воспроизвел ту же проблему без использования структурированных привязок, поэтому я оставил здесь красивый код. Пример см. здесь
decltype
: Если аргумент представляет собой выражение id без скобок, именующее структурированное привязка, то decltype возвращает ссылочный тип. Но я признаю, что понятия не имею, в чем причина этого. - person HolyBlackCat   schedule 21.10.2018