6

As I understand temporaries, the following code should work, but it doesn't.

struct base
{
    virtual~base() {}
    virtual void virt()const=0;
};
struct derived:public base
{
    virtual void virt()const {}
};

const base& foo() {return derived();}

int main()
{
    foo().virt();
    return 0;
}

The call to virt() gives a "pure virtual function called" error. Why is that, and what should I do?

4

6 回答 6

8

You're returning a reference to a temporary which is destructed when the function ends at the end of the return statement and you get undefined behaviour.

You cannot return any kind of reference to a temporary and if you return a base by value you'll get slicing, so if you really want this to work, you should return a std::unique_ptr<base>.

于 2013-01-23T22:53:13.423 回答
5

It seems like you're expecting the const reference to extend the lifetime of the temporary. There are certain situations where this doesn't occur. One of those situations is when returning a temporary:

The second context [in which temporaries are destroyed at a different point than the end of the full-expression] is when a reference is bound to a temporary. The temporary to which the reference is bound or the temporary that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference except:

[...]

  • The lifetime of a temporary bound to the returned value in a function return statement (6.6.3) is not extended; the temporary is destroyed at the end of the full-expression in the return statement.

Since calling a member function of the object returned by foo() will necessitate an lvalue-to-rvalue conversion and the object is invalid (not derived from type base), you get undefined behaviour:

If the object to which the glvalue refers is not an object of type T and is not an object of a type derived from T, or if the object is uninitialized, a program that necessitates this conversion has undefined behavior.

于 2013-01-23T22:56:06.670 回答
3

I don't think you can return references to objects like that. The derived() instance goes out of scope as soon as foo() returns. Have

base *foo() { return new derived(); }

and

foo()->virt();

should work.

于 2013-01-23T22:54:29.153 回答
0

There is no object during the call, the local object was deleted. There will be no vtable to look at. Try:

base& foo() { return *new derived(); }
于 2013-01-23T22:56:45.213 回答
0

Your temporary 'derived()' object is allocated on the stack. It might have something to do with the object up-casting.

const base& foo() { derived *d = new derived(); return *d; }

Works just fine, for me.

于 2013-01-23T22:57:11.593 回答
0

Change the problem piece to this:

class A {
public:
  const base &foo() { return d; }
private:
  derived d;
};

This way the lifetime of the derived object is as long as A's lifetime, and there will never be any problems.

于 2013-01-23T23:12:09.033 回答