1

There is a complaint that the following function doesn't work because it returns "pointers/iterators to local variables". Is this complaint correct? I can't see this problem...

const Range dummy::foo() const
{
    std::vector<Handle> _handles;
    _handles.reserve(_collection.size());

    for (const auto& pair: _collection)
    {
        _handles.push_back(pair.first);
    }

    return _handles;
}

Return type:

using Range = boost::any_range<Handle, boost::forward_traversal_tag, const Handle>;

Thanks for explanations and suggested solutions!

scohe001
  • 15,110
  • 2
  • 31
  • 51
  • 6
    What is type of `Range` ? – Ghasem Ramezani Jan 05 '21 at 17:25
  • 3
    You are never allowed to return a reference to a function local variable. All function loval variables are destroyed once the function returns leaving any reference to them dangling. – NathanOliver Jan 05 '21 at 17:26
  • *There is a complaint that the following function doesn't work* -- Who made the complaint? C++ has something called value-semantics, and from all looks of it, you are returning a value, not a pointer, iterator, or reference. – PaulMcKenzie Jan 05 '21 at 17:31
  • My guess would be that `boost::any_range` just holds an iterator pair as opposed to a copy of the entire vector, and those iterators become invalid when the function returns. – Miles Budnek Jan 05 '21 at 17:36
  • @GhasemRamezani sorry. I added it. – PythonLinski Jan 05 '21 at 17:45
  • @NathanOliver and how could I change my code? Should I define _handles globally? – PythonLinski Jan 05 '21 at 17:47
  • @PythonLinski What not just return the vector from the function? – NathanOliver Jan 05 '21 at 17:48
  • 1
    Change `const Range dummy::foo() const` to `std::vector dummy::foo() const` – Eljay Jan 05 '21 at 17:48
  • foo is a function defined in an interface which multiple other classes implements and where the Range is required. – PythonLinski Jan 05 '21 at 17:54
  • @PythonLinski If a `Range` can be implcitly created from a `vector` lie you do in your function, then you can still return a vector and the call site will do the conversion for you. – NathanOliver Jan 05 '21 at 18:14

2 Answers2

2

Is this complaint correct?

Yes it is. _handles is an automatic variable, and you return a range referring to it. Ranges are basically abstractions over pairs of iterator + sentinel. The returned range will be invalid outside the function.

And how can I solve this

A correct implementation would be to return a transforming adaptor range. Possibly something along the lines of:

return _collection | boost::adaptors::map_keys;
eerorika
  • 232,697
  • 12
  • 197
  • 326
  • And how can I solve this if the Range as return type is required? – PythonLinski Jan 05 '21 at 18:04
  • Minor nit: the oneliner `return _collection | boost::adaptors::map_keys;` is more direct. Also, some [historical context in my answer](https://stackoverflow.com/a/65585222/85371) – sehe Jan 05 '21 at 19:21
  • 1
    @sehe Thanks. I've edited the answer. I did guess that the more specific adaptor might already exist in Boost, but couldn't find it in a minute of searching. – eerorika Jan 05 '21 at 19:32
1

Interestingly, I predicted this a month ago in my answer here: How to convert a single object to a boost::any_range?

The problem is much the same as your initializer_list:

That approach invokes Undefined Behaviour because the initializer list doesn't exist after returning.

Here, the same applies to the vector.

Instead of the iterator-wrapping range you can of course simply return the vector itself. Otherwise you could emploiy the exact same techniques I used in that other answer where I constructed a singleton range (with value-semantics).

sehe
  • 374,641
  • 47
  • 450
  • 633