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

Han*_*sch 14 c++ unordered-map language-lawyer c++17 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&>);
Run Code Online (Sandbox Code Playgroud)

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&>);
Run Code Online (Sandbox Code Playgroud)

然而,在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;
}
Run Code Online (Sandbox Code Playgroud)

例如,该程序将打印(忽略订单):

0; 1
1; 2
2; 3
Increment:
0; 2
1; 3
2; 4
Run Code Online (Sandbox Code Playgroud)

我错过了什么?尽管局部变量不是引用类型,但它如何改变映射中的值?或者更恰当的是,为什么std :: is_same看不到正确的类型,因为很明显它是一个引用类型?或者我是否还缺少某些未定义的行为?

请注意,我确实在不使用结构化绑定的情况下重现了同样的问题,所以我在这里保留了漂亮的代码. 请看这里的例子

T.C*_*.C. 16

结构化绑定被建模为别名,而不是"真实"引用.尽管他们可能会在引擎盖下使用引用.

想象一下,你有

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

X x;
X& y = x;
Run Code Online (Sandbox Code Playgroud)

什么decltype(x.second)int.什么decltype(y.second)int.等等

auto& [first, second, third] = x;
Run Code Online (Sandbox Code Playgroud)

decltype(second)是的int,因为second是别名x.second.并third提出,即使它不能绑定到一个位字段的引用没有问题,因为它是一个别名,而不是实际的参考.

类似元组的案例旨在与之保持一致.即使在这种情况下语言必须使用引用,它也会尽力假装这些引用不存在.


归档时间:

查看次数:

796 次

最近记录:

7 年,7 月 前