Can a function take an argument by const reference *without* accepting temporaries as an argument?

Viewed 54

Motivating example:

Suppose we have a class Foo and a class ObservesFoo that has a non-owning pointer to an instance of a Foo. We could write that as follows:

class Foo {};

class ObservesFoo
{
    Foo* p_foo = nullptr;

    ObservesFoo(Foo* t_foo) : p_foo(t_foo) { }
};

But maybe I don't consider nullptr to be a valid value for p_foo to have. I can write a different constructor for ObservesFoo that'll enforce this requirement without me needing to do something like throwing if t_foo is invalid:

ObservesFoo(Foo& t_foo) : p_foo(&t_foo) { }

Then I say to myself, "Hang on, this constructor isn't changing t_foo. Shouldn't I take it as a const Foo&?" But then if I write the function like this, it might accept a temporary Foo as its argument, whose lifetime would immediately end as soon as the constructor returned, which would obviate any guarantees I got from ensuring that p_foo wasn't nullptr, because now it could very easily be a dangling pointer.

Is there a clean way to get the middle ground between the Foo& constructor and the const Foo& constructor?

1 Answers

Assuming const doesn't interfere with how you use the object, and you never need to rebind what object is being observed, and the observed Foo instance is guaranteed to outlive its observer(s), then a reference will work.

If you delete the constructor taking an rvalue-reference to a Foo, you will prevent the accidental lifetime extension you mentioned:

class ObservesFoo
{
    Foo const& p_foo;

    ObservesFoo(Foo const& t_foo) : p_foo(t_foo) { }
    ObservesFoo(Foo&& t_foo) = delete;
};
Related