在unordered_map和引用上基于范围的for循环

时间:2018-10-21 11:02:14

标签: c++ language-lawyer c++17 unordered-map structured-bindings

在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

1 个答案:

答案 0 :(得分:16)

结构化绑定被建模为别名,而不是“真实”引用。即使他们可能在幕后使用参考。

想象你有

struct X {
    const int first = 0;
    int second;
    int third : 8;
};

X x;
X& y = x;

decltype(x.second)是什么? intdecltype(y.second)是什么? int。等等

auto& [first, second, third] = x;

decltype(second)int,因为secondx.second的别名。并且third不会造成任何问题,即使不允许将引用绑定到位域,因为它是别名,而不是实际引用。

类似元组的案例旨在与此保持一致。即使在这种情况下,该语言也必须使用引用,但最好还是假装这些引用不存在。