2

Not quite sure why this is happening but I have the following code:

vector<glm::vec3>& HeightMap::Vec3Vertices() {
    vector<glm::vec3> vecs;
    for (unsigned int i = 0; i < vertices.size(); i += 3) {
        vecs.push_back(glm::vec3(vertices[i], vertices[i + 1], vertices[i + 2]));
    }
    return vecs;
}

Now later in my main code I have:

vector<glm::vec3>& vertices = hm.Vec3Vertices();

Where hm is a Height Map object.

I'm in Release Mode

I use vertices later and when I do the size is correct but all the data within it is unreadable. When I step through the code it appears as though the compiler has optimized out the initialization of the vecs in Vec3Vertices()

On the other hand if I change it so that I do not pass by reference

vector<glm::vec3> HeightMap::Vec3Vertices() {

and

vector<glm::vec3> vertices = hm.Vec3Vertices();

It all works perfectly. Any ideas on why this might be? I am aware that passing by value will probably result in the compiler using a move operation anyways, I just can't for the life of me figure out why my pass by reference is invalid in the first place. Thanks in advance

Ryoku
  • 397
  • 2
  • 16
  • 1
    **Never** return a reference to a local object. The object gets destroyed when the function ends. – NathanOliver Mar 15 '18 at 23:51
  • 1
    You return a dangling reference. https://stackoverflow.com/q/46011510/1896169 – Justin Mar 15 '18 at 23:51
  • 1
    But a vector is just a pointer to data on the heap so why can't I pass it back? I mean the data stille xists on the heap doesnt it? On a similar note what semantics should I use to ensure a move is used? – Ryoku Mar 15 '18 at 23:53
  • 3
    Yes, and the vector's destructor deletes the pointer when it is destroyed – NathanOliver Mar 15 '18 at 23:53
  • 1
    Because a vector is not just a pointer to data on the heap. – juanchopanza Mar 15 '18 at 23:53
  • @Ryoku To ensure a move is used (when you return a value, not a reference), just return it. It will automatically be moved, or possibly better (sometimes the compiler can completely elide the move/copy) – Justin Mar 15 '18 at 23:55
  • @Justin okay I think I get it. The pointer is destroyed. That sucks. On the other hand I know the compiler is probably smart enough to make a good call on it, but out of curiosity is there a way to ensure a move is used? – Ryoku Mar 15 '18 at 23:58
  • 1
    Actually, with C++17 your code won't even involve a move. The code will invoke a "Return Value Optimization" which means: the caller allocates space for the return value, then the function constructs the returned object directly in that space which the caller provided. (This is due to the specific way the function is written, in which there is a single return statement, which is returning a local variable of the same type as the function's return value.) – Daniel Schepler Mar 15 '18 at 23:58
  • @Daniel thats awesome, took many a years to get that level of efficiency – Ryoku Mar 16 '18 at 00:00
  • @DanielSchepler Even in C++98 it would probably just be NRVO'd. – juanchopanza Mar 16 '18 at 00:03
  • 1
    Hmm... Actually, according to http://en.cppreference.com/w/cpp/language/copy_elision - as far as I can tell this would still be an NRVO optimization, and still isn't a mandatory optimization in C++17. That said, most compilers in practice would apply the NRVO optimization. – Daniel Schepler Mar 16 '18 at 00:07
  • @juanchopanza even if it was, you still can't return a reference to a local pointer – M.M Mar 16 '18 at 00:22
  • @M.M There are many scenarios in which a reference to a valid object can be returned, but none of them solve the simple problem of returning something that gets created in a function without introducing ownership issues. – juanchopanza Mar 16 '18 at 06:29

0 Answers0