Suppose a function Foo()
takes an object of type Bar
.
Instead of
Bar bar(parameters);
Foo(bar);
You can do Foo(Bar(parameters);
Why isn't the second case allowed when Foo
is defined to take a reference i.e returntype Foo(Bar &bar);
Suppose a function Foo()
takes an object of type Bar
.
Instead of
Bar bar(parameters);
Foo(bar);
You can do Foo(Bar(parameters);
Why isn't the second case allowed when Foo
is defined to take a reference i.e returntype Foo(Bar &bar);
An lvalue reference (the kind you're referring to, written as T&
) refers to an lvalue. Now the exact details of what that means are kind of complicated in modern C++, but the basic idea is that an lvalue is a thing that you can assign to. The name originally comes from the fact that an lvalue (where "l" is short for "left") can appear on the left-hand side of an assignment statement.
Non-const variables with names can be assigned to, naturally, so they can be passed as lvalue references. But temporaries are, generally speaking, not lvalues, since they don't refer to a place that can be assigned to.
If your function isn't going to modify the argument, you can take a const T&
, a constant lvalue reference. This is very common in constructors, especially if the constructor is just going to copy the data out into its own storage anyway. If you are planning to modify the argument, then read on.
References in C++ (before C++11) sort of served double duty. They were meant as "I can assign to this position" and also as "this is a pointer-like thing that I'm borrowing and I won't modify". The second meaning was mostly captured by const T&
, constant lvalue references. But there was still one case where we needed more. Specifically, if I had a value that I was never going to use again (such as, but not necessarily, a temporary value), then I might want to pass it by (mutable) reference to a function so that function can destructure it and potentially reuse parts of it.
A prime example of this is an array-like container. Say I've got an array class, say Vector
, that consists of a pointer to some data and a size variable. If I need to make a copy of that variable, then in principle I need to copy the whole array. But if I know I'll never use the original vector again, I can instead move the vector, simply assigning the pointer and size variables directly and avoiding the need for a messy O(n) copy. But to do this, I would need exactly what you're describing. I would need a mutable pointer type that can take rvalues, or things that can't appear on the left-hand side of an assignment statement.
Enter rvalue references, which are written T&&
. These are like lvalue references except that they can take temporary locations. An rvalue reference can still modify its argument but understands that its argument is, in principle, not going to be around much longer. So you shouldn't store the address of that thing in a class, because it's not going to live much longer, but you can still do things with it, most prominently move data out of it.
Long story short, if you don't want to modify the argument, then you should write your function as
void Foo(const Bar& arg)
If you need to modify the argument, but you understand fully that the memory might not be around for much longer, then you can use an rvalue reference.
void Foo(Bar&& arg)