Search code examples
c++iteratorclangdereferencestdoptional

Is it ok to double-dereference ("**itOpt") an optional iterator?


If I have a std::optional wrapped around (say) an std::vector::const_iterator, is it safe to access the referenced element with two consecutive dereference (*) operators?

For example:

  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;

As far as I can tell, this is fine as long as ivec is not a local, but Clang complains:

warning: reference to stack memory associated with local variable 'itOpt' returned [-Wreturn-stack-address]

Complete example:

// 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

With Clang (v16, v18, and latest), I get:

$ 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 and MSVC accept this example without complaint (https://godbolt.org/z/f49P4s4fd).

Is there a hidden danger here, or is Clang just wrong?


As the consensus seems to be that this is false positive, I have filed Clang Issue #96403.


Solution

  • It's a false positive. The code is ok.

    The element referenced in the returned reference lives in ivec which is global so that's not a problem.

    One thing to be careful here is that the reference returned can get invalidated with some operations on vector like push etc. See Iterator invalidation rules for C++ containers