从临时变量中获取引用在C++代码中是否有效?

23
我使用了以下语法糖:
for (auto& numberString: {"one", "two", "three", "four"}) { /* ... */}

这段代码是否有效?据我所知,根据这个问题,它应该是不合法的,但是代码的运行结果却与预期相符。我认为自己对这个问题的理解是错误的。

据我所知,只有字面量不应该有内存地址,然而链接的问题是在谈论临时变量和r-values


8
临时的 std::initializer_list 对象实际上将拥有一个包含循环的生命周期,因此它是有效的。 - Some programmer dude
那么如果我的理解正确:只要临时变量的生命周期仍然有效,从中取一个引用就是有效的行为。在参考的问题中,没有涉及到字符串字面量,只有临时变量的生命周期相关。我理解得对吗? - Dávid Tóth
5
是的,临时量的生命周期很重要。只要临时对象的生命周期比引用的生命周期长,一切都会好的。 - Some programmer dude
1个回答

28

是的,这段代码是有效的。

请记住(对于C++17),编译器将通过构造语义上替换基于范围的for循环

{

    auto && __range = {"one", "two", "three", "four"};
    for (auto __begin = begin(__range), __end = end(__range); __begin != __end; ++__begin)
    {

        auto& numberString = *__begin;
        /* ... */
    }

}

你看,initializer_list的生命周期被延长到替换中最外层作用域内的__range的生命周期。

但请注意,如果范围表达式本身包含一个临时对象,仍然很容易导致未定义的行为:

struct some {
   auto get_list() { return {"one", "two", "three", "four"}; }
};

some foo() { return some{ }; }

for(auto& numberString : foo().get_list()) { /* ... */ }

以上代码将在C++20及以下版本中导致悬空引用。 只有在C++23中,由foo()创建的临时对象的生命周期才会得到延长,从而使其变为有效。另请参见https://en.cppreference.com/w/cpp/language/range-for

5
我非常确定你的第二个例子只有在get_list返回some的成员引用时才会是未定义的。就目前而言,foo返回一个临时的some对象,该对象的生命周期持续到完整表达式的结束,这足以在其上调用get_listget_list返回一个临时对象,然后按照通常的规则进行寿命延长。 - Miles Budnek
1
实际上,我认为即使在C++20中,第二个示例也是未定义的。initializer_list的后备缓冲区是局部于get_list的,而且没有任何initializer_list返回值的生命周期延长会改变这一点。(一旦我将返回类型更改为std::initializer_list<const char*>,GCC和Clang都会警告返回临时对象的地址) - Sebastian Redl

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接