unordered_map 和引用的基于范围的 for 循环
Range-based for loop on unordered_map and references
当 运行 在 std::unordered_map 上进行基于范围的 for 循环时,循环变量的类型似乎不使用引用类型:
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 看不到正确的类型,因为很明显它是引用类型?
或者我是否遗漏了一些未定义的行为?
请注意,我在没有使用结构化绑定的情况下重现了同样的问题,所以我在这里保留了漂亮的代码。
See here for an example
结构化绑定建模为别名,而不是 "real" 引用。尽管他们可能会在幕后使用引用。
假设您有
struct X {
const int first = 0;
int second;
int third : 8;
};
X x;
X& y = x;
什么是 decltype(x.second)
? int
。 decltype(y.second)
是什么? int
。所以在
auto& [first, second, third] = x;
decltype(second)
是 int
,因为 second
是 x.second
的别名。 third
即使不允许将引用绑定到位域也没有问题,因为它是别名,而不是实际引用。
类似元组的案例旨在与此一致。即使在那种情况下语言必须使用引用,它也会尽力假装这些引用不存在。
当 运行 在 std::unordered_map 上进行基于范围的 for 循环时,循环变量的类型似乎不使用引用类型:
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 看不到正确的类型,因为很明显它是引用类型? 或者我是否遗漏了一些未定义的行为?
请注意,我在没有使用结构化绑定的情况下重现了同样的问题,所以我在这里保留了漂亮的代码。 See here for an example
结构化绑定建模为别名,而不是 "real" 引用。尽管他们可能会在幕后使用引用。
假设您有
struct X {
const int first = 0;
int second;
int third : 8;
};
X x;
X& y = x;
什么是 decltype(x.second)
? int
。 decltype(y.second)
是什么? int
。所以在
auto& [first, second, third] = x;
decltype(second)
是 int
,因为 second
是 x.second
的别名。 third
即使不允许将引用绑定到位域也没有问题,因为它是别名,而不是实际引用。
类似元组的案例旨在与此一致。即使在那种情况下语言必须使用引用,它也会尽力假装这些引用不存在。