变量循环范围会导致返回局部变量的地址引用

ranged for loop of variables results in returning address-reference of local variable?

本文关键字:局部变量 地址 引用 返回 循环 范围 变量      更新时间:2023-10-16
// g++ --std=c++17 test.cpp -I /usr/local/include -L /usr/local/lib -lboost_system -Wall -pedantic -Wreturn-type -Wstrict-aliasing -Wreturn-local-addr -fsanitize=address -g
// LD_LIBRARY_PATH=/usr/local/lib ./a.out
#include <iostream>
#include <boost/filesystem.hpp>
namespace fs = boost::filesystem;
class A {
public:
    fs::path path_;
    const fs::path & path() const { return path_; }
    fs::path & path() { return path_; }
};
class B {
public:
    fs::path root_path_;
    A path_2;
    A path_3;
    const fs::path & operator()() const {
        for ( const auto & path : {
            path_3.path(),
            path_2.path(),
            root_path_
        }) {
            if ( not path.empty() ) {
                return path;
            }
        }
        throw std::logic_error{"using default-constructed B"};
    }
};
int main(int argc, char **argv) {
    B b;
    b.root_path_ = "foo/";
    b.path_2.path() = "foo/bar";
    b.path_3.path() = "foo/baz";
    std::cout << b() << 'n';
    return 0;
}

使用上述代码,据我所知,它似乎是有效的C 。相反,当调用时,我会收到垃圾输出。

g++最初不抱怨,但是地址消毒剂 di 。添加-O2时,g++最终抱怨。产生的警告是

test.cpp: In member function ‘const boost::filesystem::path& B::operator()() const’:
test.cpp:31:12: warning: function may return address of local variable [-Wreturn-local-addr]
     return path;
            ^~~~
test.cpp:29:3: note: declared here
   }) {
   ^

请注意,我正在使用:

$ cat /etc/fedora-release 
Fedora release 25 (Twenty Five)
$ g++ --version
g++ (GCC) 6.3.1 20161221 (Red Hat 6.3.1-1)
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

请注意,我通过使用指针解决了错误

    const fs::path & operator()() const {
            for ( const auto * path : {
                    &path_3.path(),
                    &path_2.path(),
                    &root_path_
            }) {
                    if ( not path->empty() ) {
                            return *path;
                    }
            }
            throw std::logic_error{"using default-constructed B"};
    }

但是,这确实留下了一些问题:

  1. 为什么 g++ 不是 在添加 -O2之前会抱怨问题?
  2. 到底我的代码不确定吗?我想说的是明确的:B::operator() const是...嗯... const。这应该意味着其中使用的对象是当地人或const成员。它访问const成员。它构造了局部变量const auto &,该变量应引用const成员字段。到底是什么原因导致它与临时绑定?
  1. 编译器没有义务发布不确定行为的诊断。如果编译器可以检测到句法有效的代码,但会导致不确定的行为,然后抱怨,那只是在蛋糕之上锦上添花。GCC的-O2打开了其他优化,并进行其他代码分析;因为这样的海湾合作委员会有时只有在启用了优化时才能检测到未定义的行为。

  2. 看来,您的范围迭代位于临时 std::initializer_list上。范围迭代变量是初始化列表中的引用。因此,此功能最终会返回对临时性的引用,这是GCC正在吠叫的内容。由于该方法返回时临时性被摧毁,因此该方法最终会返回对被破坏对象的引用。该参考的任何使用都包括不确定的行为。

当您将临时范围转换为指针列表时,您将按值进行迭代,而您没有返回对临时性的引用,而是在范围内放弃该值,这是一个完美的犹太洁食指针。p>请注意以下内容:

在生命周期之后,不保证基础阵列 原始初始化列表对象已经结束。存储 std :: initializer_list未指定(即它可以是自动的, 根据情况,临时或静态读取的内存)。

生命周期如果初始化列表绑定到范围迭代的范围,则在迭代的结论结束时结束。其中包括从该方法返回。因此,初始化列表不再存在,您刚刚返回了一个悬空的参考。