代码之家  ›  专栏  ›  技术社区  ›  Scott McPeak

可以对可选迭代器进行双重取消引用(“**itOpt”)吗?

  •  2
  • Scott McPeak  · 技术社区  · 11 月前

    如果我有 std::optional 裹在(比如)一个 std::vector::const_iterator ,访问引用的是否安全 具有两个连续取消引用的元素( * )操作员?

    例如

      typedef std::vector<int> IVec;
    
      // Get a valid iterator.
      IVec::const_iterator it = ivec.cbegin();
      assert(it != ivec.end());
    
      // Wrap it in an optional.
      std::optional<IVec::const_iterator> itOpt(it);
      assert(itOpt);
    
      // Access the iterator via double dereference.
      return **itOpt;
    

    据我所知,只要 ivec 不是本地的,但是 Clang抱怨道:

    警告:返回了对与本地变量“itOpt”关联的堆栈内存的引用[-Wreturn堆栈地址]

    完整示例:

    // test.cc
    // Clang complains when accessing an optional iterator.
    
    #include <cassert>                     // assert
    #include <iostream>                    // std::cout
    #include <optional>                    // std::optional
    #include <vector>                      // std::vector
    
    typedef std::vector<int> IVec;
    IVec ivec;
    
    int const &getVecElementRef()
    {
      // Get a valid iterator.
      IVec::const_iterator it = ivec.cbegin();
      assert(it != ivec.end());
    
      // Wrap it in an optional.
      std::optional<IVec::const_iterator> itOpt(it);
      assert(itOpt);
    
    #if 1
      // Access the iterator via double dereference.
      //
      // Clang complains: warning: reference to stack memory associated with
      // local variable 'itOpt' returned [-Wreturn-stack-address]
      return **itOpt;
    
    #else
      // This works fine (with or without the `const &` part).
      auto const &tmp = *itOpt;
      return *tmp;
    #endif
    }
    
    int main()
    {
      ivec.push_back(0);
    
      // Get an element reference using operator[].
      int const &v0 = ivec[0];
      std::cout << "&v0: " << &v0 << "\n";
    
      // Get an element reference using the function.  The resulting address
      // is the same.
      int const &v1 = getVecElementRef();
      std::cout << "&v1: " << &v1 << "\n";
    
      // Returns 0.
      return v1;
    }
    
    // EOF
    

    使用Clang(v16、v18和最新版本),我可以:

    $ clang++ -o test.exe  -std=c++17 -g -Wall test.cc
    test.cc:27:12: warning: reference to stack memory associated with local variable 'itOpt' returned [-Wreturn-stack-address]
      return **itOpt;
               ^~~~~
    1 warning generated.
    
    $ ./test.exe
    &v0: 0x5633e9c95eb0
    &v1: 0x5633e9c95eb0
    

    GCC和MSVC毫无怨言地接受了这个例子 ( https://godbolt.org/z/f49P4s4fd ).

    这里有隐患吗?还是Clang错了?


    由于普遍认为这是假阳性,我已经提交了Clang Issue #96403 .

    1 回复  |  直到 11 月前
        1
  •  3
  •   bolov    11 月前

    这是一个假阳性。代码正常。

    返回的引用中引用的元素位于 ivec 这是全球性的,所以这不是问题。

    这里需要注意的一点是,通过对向量进行一些操作,返回的引用可能会无效,比如 push 等等。请参阅 Iterator invalidation rules for C++ containers