tags:

views:

163

answers:

3

My program has this function:

    vector<itemPtr> Level::getItemsAt(const Point& pt)
    {
        vector<itemPtr> vect(items.size());
        // copy all items at pt's position to vect
        remove_copy_if(items.begin(), items.end(), vect.begin(),
                       boost::bind(matchesPosition<itemPtr>, _1, pt));

        // update LevelMap and return
        map.setHasItem(pt, false);
        return vect;
    }

This compiles fine (I'm using g++, my gcc version is 4:4.4.1-1ubuntu2), but when I run the program it skips right over the return statement.

I stepped through with gdb, setting a breakpoint at the previous line, and got this:

Breakpoint 1, yarl::level::Level::getItemsAt (this=0x80d4d58, pt=...)
at src/Level.cpp:519
519                 map.setHasItem(pt, false);
(gdb) next
521             }
(gdb) 

I've tried recompiling from scratch several times, erasing the executable and all the object files beforehand, and it still does it.

Strangely, if I comment out the return statement and try to compile, it only gives warning: no return statement in function returning non-void. I would have thought not providing a return statement in a function that returns something would be a compiler error, but I guess not.

I realize this is not much to go on, but does anyone have an idea why this is happening? What to check for? At this point I don't even know where to start looking.

EDIT: for clarification, I'm compiling with -O0.

According to tjm, my version of gcc will still use RVO even with a -O0 compiler flag, so that was the problem after all. Thanks for your help, guys.

+6  A: 

C++ source code does not have to correspond to resulting object code one-to-one as long as the behavior is preserved. What's happening here is that the compiler rearranges the code, and probably invokes Return Value Optimization.

Edit:

Add this to the GCC options: -fdump-tree-nrv to see NRVO applications by the compiler (you'll get a file with .nrv extension). This only works with optimization level greater then -O0.

Without optimization, replacing the return statement with copy constructor or copy assignment operator call is still a C++ front-end transformation, which is not gracefully handled by the gdb.

Nikolai N Fetissov
I'm compiling with `-O0`, though.
Max
@Max, i don't know much about RVO, but if the first example in Nikolai's link is a valid test as to whether that is occurring, when I compile it here, it seems that gcc 4.4.4 will do it even if you compile with `-O0`.
tjm
@tjm: Interesting. So it seems gcc is using RVO regardless of `-O0` or not. This probably answers my question.@Nikolai: I compiled and ran with `-fdump-tree-nrv` and without `-O0`, but I don't see a .nrv file anywhere. Where does it go?
Max
It should be in the same directory with the same name as your source file. It needs at least `-O1` (no optimization flag means no optimization). The generated file is a hard read though :)
Nikolai N Fetissov
+1  A: 

I can think of a couple of reasons why the return statement may be skipped over - could any of these be the case?

  • An exception is thrown on line 519. This sounds very unlikely given line 521 is hit.
  • The compiler has optimised away the return statement line - are you debugging a proper debug build (no optimisations enabled)?

Does the function work fine - does the callee get the result back and carry on its merry way?

Will A
My program segfaults once the callee tries to use the vector that the function was returning. I was assuming it was because my program was trying to use an empty vector, but stepping through after the function call shows the callee is a correctly sized vector. It would sound like it was Return Value Optimization after all, except that I'm using `O0`.
Max
A: 

Are map and pt correctly allocated and initialized objects?

If an exception is thrown from line 519 then the method won't return normally. In this case you might expect the next line "executed" to be line 521 as the stack unwinds.

I've seen quite a lot of this in Visual C++ when the code does something that results in "undefined behavior". I don't have enough experience with g++ to know if the observed behavior is the same.

richj
no exception is thrown. My program eventually segfaults after it iterates through the vector returned from that function. Although that problem seems unrelated to the skipped return statement.
Max