为什么在这里创建对prvalue的左值引用是合法的?

tex*_*uce 14 c++ language-lawyer c++11

我的代码如下:

#include <vector>
#include <iostream>


int main(){
    for(int& v : std::vector<int>{1,3,5,10}) {
        std::cout << v << std::endl;
        v++; // Does this cause undefined behavior?
    }
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

据我所知,向量是prvalue,并且无法绑定int&,但是这个可以正常工作吗?

是因为范围循环只是宏扩展而且会为向量创建临时变量吗?

tem*_*def 17

根据C++ ISO规范,基于范围的for循环被正式定义为等价于

{
    auto && __range = range_expression ; 
    for (auto __begin = begin_expr, __end = end_expr; 
         __begin != __end; ++__begin) {
         range_declaration = *__begin; 
         loop_statement 
    } 
}
Run Code Online (Sandbox Code Playgroud)

请注意,"等效于"并不意味着"扩展为宏",而是"具有与"完全相同的行为".这种等价不是传统意义上的宏(即它不是a #define),而是在ISO规范中给出,作为定义基于范围的for循环的语义的形式方式.基于范围的for循环的实现可以是编译器喜欢的任何内容,只要该行为与上面给出的行为完全相同即可.

在这个上下文中,range_expression是一个prvalue,但它然后被绑定__range,这是一个左值.扫描得到的迭代器__range也是左值,因此当取消引用迭代器以产生范围内的各个值时,这些值将是左值.

希望这可以帮助!

  • 你是什​​么意思"但是左值引用充当左值引用"?这听起来比"`__range`是一个名字更令人困惑,因此它是一个左值." (2认同)
  • @texasbruce上面发布的代码不是宏 - 它实际上取自C++标准 - 而是基于范围的for循环的正式定义.具体来说,如果要扩展和替换,基于范围的for循环的行为应该与上面显示的代码完全相同.但这并不意味着它是一个宏观; 语言定义为"(range_declaration:range_expression)loop_statement`的语句`等同于上面的代码块." 那有意义吗? (2认同)

Rei*_*ica 9

基于范围的for循环绝对不是宏扩展.它是该语言的独立构造.尽管矢量本身是一个prvalue,但它的成员函数仍然正常运行.所以它operator[](或解除引用它的迭代器)返回一个正常的左值引用等.

当然,只要载体本身存在,这样的引用才有效.它的寿命持续整个基于范围的for循环(这是for标准中基于范围的循环规范所要求的),所以一切都很好.

就价值类别而言,它与此相同(这也是合法的):

int &i = std::vector<int>{1, 2, 3}[0];
Run Code Online (Sandbox Code Playgroud)

当然,与基于范围的for循环中的那个不同,该i引用立即变得悬空.但原则是一样的.

还要考虑这一点:语言无法知道迭代器operator *或向量返回的左值引用operator[]是指其生命周期与向量的生命周期绑定的东西.它只返回一个左值引用,因此它是可绑定的.


K-b*_*llo 6

基于范围的for循环不是宏扩展,而是真正的语言特性.它确实将临时的寿命延长到整个身体.