(rvalue reference) VS (const lvalue reference) as function parameters in C++11 - c++

Can someone explain when rvalue references are preferred over const lvalue references when working as function parameters?
Background:
I was trying to pass a const pointer into a function. Since I have to consider the cases in which a local pointer is passed in and in which a temporary is passed in (say from a function call return), I have two choices: the parameter could either be declared as:
void foo(T const* const&); //const lvalue ref to const ptr
or
void foo(T const* &&); //rvalue ref to const ptr
But this rvalue reference cannot be bound to a local variable (which is of lvalue type. But I did remember Scott Meyers coined the term "universal reference" to refer to rvalue reference. This confuses me more.) So my question is, since the first declaration could deal with both cases, when would the second one using rvalue reference be preferred?
Note: In the first approach, the other forms
void foo(const const T* &);
void foo(const T* const&);
didn't work. I guess the reason is that in the latter two I was not consistent in the place where the const qualifiers are put into (please correct me if I'm wrong).

It is very rarely a good idea to pass a pointer by const &: at best it takes the same overhead, at worst it causes extremely complex pointer reseating logic to surprise readers of your code.
Take pointers by value -- T const* -- and things are more sane.
References to non-pointer values make more sense.
Universal references is a technique using rvalue and lvalue references in a type deduction context. It basically only applies when you have a type T&& being deduced from an expression -- in that context T can be X, X& or X const& (or other cv variants).
If T is X& or X const&, the rvalue reference to the lvalue reference collapses into a lvalue reference. It is an example of the standard committee being clever, and it allows auto&&x= based universal reference variables, and perfect forwarding code to be easy to write.

Remark: I wrote this answer under the assumption that T in your question represents some actual data type -- I've chosen int in my examples below.
Background: I was trying to pass a const pointer into a function. [...] I have to consider the cases in which a local pointer is passed in and in which a temporary is passed in (say from a function call return)
You didn't say what you mean by "const pointer". I will first assume you mean a pointer that is itself constant (i.e. the address to which it points cannot be changed).
According to your description, there are basically two ways you get such a pointer:
// Case 1 (what you call a local pointer -- this should be inside some
// function body):
int *const p = 0;
// Case 2, a function that returns a pointer; this is your rvalue case
// in contexts where f() is called and its return value used as a temporary:
int *f()
{ return 0; }
// Note: The temporary returned by this function isn't, strictly speaking,
// constant. It could be modified as long as it is alive. But from
// your description I take it that you have no intentions of doing so
// and/or regard temporaries as generally constant.
Now you can define a function that accepts these two cases as follows:
void g(int *const &arg)
{ }
You can apply this as g(p); to a constant, local pointer such as p defined earlier, as well as to a temporary g(f());. (You could, thirdly, apply it to a non-const local pointer as well, because going from non-const lvalue to const lvalue is never a problem.)
This function g has a function argument arg which is defined as a constant, lvalue reference to an int-pointer. It can bind to a constant (or indeed non-constant) local pointer (such as p) as well as a temporary, because constant lvalue references, unlike non-constant lvalue references, can do that.
Remark: It's not clear to me why, in this case, you need the function argument to be a reference at all. You could simply declare void g(int *const arg) (no ampersand) and do without a reference. Reasons include a) You cannot modify it anyway; b) In all real-world implementations, the reference will take just as much (or as little) space as the pointer itself, so there is no point in avoiding a copy.
Anyway. If you want you can also define a second version of g specifically for rvalue references:
void g(int *&& arg)
{ }
This can only be applied to the temporary, not to the local pointer, because the function argument is defined as an rvalue reference, which can bind to temporaries, but not to lvalues.
However, if by "const pointer" you actually mean a pointer-to-const, i.e. a pointer that can be changed to different addresses, but does not have the power to modify the value stored at those addresses, the declarations are a bit different. The keyword const must then be put before the asterisk, and for better clarity best before the type specifier int:
// Declare local pointer-to-const:
const int *p = 0;
// Function that returns a pointer-to-const:
const int *f()
{ return 0; }
A function that can accept these two would then be declared as:
void g(const int *const &arg)
{ }
The first const means we are talking about pointers-to-const, and the second const ensures we have a constant lvalue-reference, which can bind to both rvalues and lvalues. Note that this function can not modify what arg points to, because arg is declared as a constant lvalue reference. In the case where arg binds to the temporary, that is probably what we want anyway (as stated above). But in the case where the function is called as g(p);, we might actually want to modify the local pointer p from within g. If you want g to have this power, you need to define two versions of it:
void g(const int *&& arg)
{ /* Can bind to temporaries, but not modify them. */ }
void g(const int *& arg)
{ /* Can bind to local variables and modify what they point at */ }
Remark 1: Your original declaration const int *const &const is useless (and not even accepted by GCC). It would mean a "constant reference to a constant pointer to constant int", but since a reference to a constant pointer is implicitly itself a const-reference, the final const is superfluous (and not provided for by the Standard).
Remark 2: Universal references are not the same as rvalue references. Universal references are declared as T &&arg where T is a template parameter. Depending on what T refers to in each instantiation of the template, this may be an lvalue reference or an rvalue reference -- hence its "universal" character. This has nothing to do with your use case, anyway, though, since you are dealing with pointers T * here (even if we assume that T is a template parameter).

You do not want to differentiate between a temporary pointer and an lvalue pointer. That looks to me like something that's bound to fail rather sooner than later.
Universal Refs only apply in template functions like
template<class T> void foo(T && fwdref);
Note that a "universal ref" is not the same as an rvalue ref.

Related

Why when r-value reference is being assigned to is considered as an l-value reference?

Consider the following code: (compile)
void f(int&&) {}
template<class T>
void s(T&& value)
{
using rv_ref = decay_t<T>&&;
static_assert( is_same_v<decltype(value), rv_ref> );
f(static_cast<rv_ref>(value)); // ok, but we've checked `value` is already of this type
//f(value); // error: no matching function
}
int main()
{
s(9);
}
What makes me flinch is the fact that even though the type of value is int&&, when it's being used as a value [and not an expression(as with decltype)], it suddenly becomes an l-value.
How is this explained to make sense?
You write this:
even though the type of value is int&&, when it's being used as a value [and not an expression(as with decltype)], it suddenly becomes an l-value.
One high-level way to think about lvalues and rvalues is that lvalues have names and can be assigned to. value obviously has a name, so it shouldn't be surprising that it's an lvalue.
One way to think of std::move() or your static cast is that it not only casts its argument to the correct type, but it also produces an rvalue expression.
Thinking about your two functions s and f:
Presumably, your function s takes an rvalue reference because you want to operate on rvalues, e.g. steal their resources with a move operation.
In such a function, you might want to call functions on value that either (i) steal its resources, or (ii) treat it as an lvalue and do not steal its resources.
The language lets you call std::move() for case (i), to tell f that f may steal resources.
The language lets you pass value to functions without std::move(), as an lvalue, for case (ii), so you can call functions without worrying about that. This might be desirable if you want s to steal resources later on.
This question is pretty similar: Rvalue Reference is Treated as an Lvalue?
The name of a variable used as an expression is always an lvalue, even if the variable's type is an rvalue reference. (Though decltype has some additional rules, not just looking at the expression's value category.) This is an intentional choice of the C++ language, because otherwise it would be too easy to accidentally move from a variable.
For example:
void debug_str_val(std::string var_name, std::string value);
void MyClass::set_name(const std::string& name)
{
debug_str_val("name", name);
m_name = name;
}
void MyClass::set_name(std::string&& name)
{
debug_str_val("name", name); // *
m_name = std::move(name);
}
On the line marked with a *, name is treated as an lvalue, so debug_str_val gets a copy of the value. (It maybe could use const std::string& parameters instead, but it doesn't.) If name were treated as an rvalue, the debug_str_val would be moved from, and then an unpredictable value would be assigned to m_name.
Once an object has a name, it could be used more than once, even if that name is an rvalue reference. So C++ uses it as an lvalue by default and lets the programmer say when they want it to be used as an rvalue, using std::move, or sometimes std::forward, or an explicit cast to rvalue reference type.

Passing rvalue reference in function argument [duplicate]

I think there's something I'm not quite understanding about rvalue references. Why does the following fail to compile (VS2012) with the error 'foo' : cannot convert parameter 1 from 'int' to 'int &&'?
void foo(int &&) {}
void bar(int &&x) { foo(x); };
I would have assumed that the type int && would be preserved when passed from bar into foo. Why does it get transformed into int once inside the function body?
I know the answer is to use std::forward:
void bar(int &&x) { foo(std::forward<int>(x)); }
so maybe I just don't have a clear grasp on why. (Also, why not std::move?)
I always remember lvalue as a value that has a name or can be addressed. Since x has a name, it is passed as an lvalue. The purpose of reference to rvalue is to allow the function to completely clobber value in any way it sees fit. If we pass x by reference as in your example, then we have no way of knowing if is safe to do this:
void foo(int &&) {}
void bar(int &&x) {
foo(x);
x.DoSomething(); // what could x be?
};
Doing foo(std::move(x)); is explicitly telling the compiler that you are done with x and no longer need it. Without that move, bad things could happen to existing code. The std::move is a safeguard.
std::forward is used for perfect forwarding in templates.
Why does it get transformed into int once inside the function body?
It doesn't; it's still a reference to an rvalue.
When a name appears in an expression, it's an lvalue - even if it happens to be a reference to an rvalue. It can be converted into an rvalue if the expression requires that (i.e. if its value is needed); but it can't be bound to an rvalue reference.
So as you say, in order to bind it to another rvalue reference, you have to explicitly convert it to an unnamed rvalue. std::forward and std::move are convenient ways to do that.
Also, why not std::move?
Why not indeed? That would make more sense than std::forward, which is intended for templates that don't know whether the argument is a reference.
It's the "no name rule". Inside bar, x has a name ... x. So it's now an lvalue. Passing something to a function as an rvalue reference doesn't make it an rvalue inside the function.
If you don't see why it must be this way, ask yourself -- what is x after foo returns? (Remember, foo is free to move x.)
rvalue and lvalue are categories of expressions.
rvalue reference and lvalue reference are categories of references.
Inside a declaration, T x&& = <initializer expression>, the variable x has type T&&, and it can be bound to an expression (the ) which is an rvalue expression. Thus, T&& has been named rvalue reference type, because it refers to an rvalue expression.
Inside a declaration, T x& = <initializer expression>, the variable x has type T&, and it can be bound to an expression (the ) which is an lvalue expression (++). Thus, T& has been named lvalue reference type, because it can refer to an lvalue expression.
It is important then, in C++, to make a difference between the naming of an entity, that appears inside a declaration, and when this name appears inside an expression.
When a name appears inside an expression as in foo(x), the name x alone is an expression, called an id-expression. By definition, and id-expression is always an lvalue expression and an lvalue expressions can not be bound to an rvalue reference.
When talking about rvalue references it's important to distinguish between two key unrelated steps in the lifetime of a reference - binding and value semantics.
Binding here refers to the exact way a value is matched to the parameter type when calling a function.
For example, if you have the function overloads:
void foo(int a) {}
void foo(int&& a) {}
Then when calling foo(x), the act of selecting the proper overload involves binding the value x to the parameter of foo.
rvalue references are only about binding semantics.
Inside the bodies of both foo functions the variable a acts as a regular lvalue. That is, if we rewrite the second function like this:
void foo(int&& a) {
foo(a);
}
then intuitively this should result in a stack overflow. But it doesn't - rvalue references are all about binding and never about value semantics. Since a is a regular lvalue inside the function body, then the first overload foo(int) will be called at that point and no stack overflow occurs. A stack overflow would only occur if we explicitly change the value type of a, e.g. by using std::move:
void foo(int&& a) {
foo(std::move(a));
}
At this point a stack overflow will occur because of the changed value semantics.
This is in my opinion the most confusing feature of rvalue references - that the type works differently during and after binding. It's an rvalue reference when binding but it acts like an lvalue reference after that. In all respects a variable of type rvalue reference acts like a variable of type lvalue reference after binding is done.
The only difference between an lvalue and an rvalue reference comes when binding - if there is both an lvalue and rvalue overload available, then temporary objects (or rather xvalues - eXpiring values) will be preferentially bound to rvalue references:
void goo(const int& x) {}
void goo(int&& x) {}
goo(5); // this will call goo(int&&) because 5 is an xvalue
That's the only difference. Technically there is nothing stopping you from using rvalue references like lvalue references, other than convention:
void doit(int&& x) {
x = 123;
}
int a;
doit(std::move(a));
std::cout << a; // totally valid, prints 123, but please don't do that
And the keyword here is "convention". Since rvalue references preferentially bind to temporary objects, then it's reasonable to assume that you can gut the temporary object, i.e. move away all of its data away from it, because after the call it's not accessible in any way and is going to be destroyed anyway:
std::vector<std::string> strings;
string.push_back(std::string("abc"));
In the above snippet the temporary object std::string("abc") cannot be used in any way after the statement in which it appears, because it's not bound to any variable. Therefore push_back is allowed to move away its contents instead of copying it and therefore save an extra allocation and deallocation.
That is, unless you use std::move:
std::vector<std::string> strings;
std::string mystr("abc");
string.push_back(std::move(mystr));
Now the object mystr is still accessible after the call to push_back, but push_back doesn't know this - it's still assuming that it's allowed to gut the object, because it's passed in as an rvalue reference. This is why the behavior of std::move() is one of convention and also why std::move() by itself doesn't actually do anything - in particular it doesn't do any movement. It just marks its argument as "ready to get gutted".
The final point is: rvalue references are only useful when used in tandem with lvalue references. There is no case where an rvalue argument is useful by itself (exaggerating here).
Say you have a function accepting a string:
void foo(std::string);
If the function is going to simply inspect the string and not make a copy of it, then use const&:
void foo(const std::string&);
This always avoids a copy when calling the function.
If the function is going to modify or store a copy of the string, then use pass-by-value:
void foo(std::string s);
In this case you'll receive a copy if the caller passes an lvalue and temporary objects will be constructed in-place, avoiding a copy. Then use std::move(s) if you want to store the value of s, e.g. in a member variable. Note that this will work efficiently even if the caller passes an rvalue reference, that is foo(std::move(mystring)); because std::string provides a move constructor.
Using an rvalue here is a poor choice:
void foo(std::string&&)
because it places the burden of preparing the object on the caller. In particular if the caller wants to pass a copy of a string to this function, they have to do that explicitly;
std::string s;
foo(s); // XXX: doesn't compile
foo(std::string(s)); // have to create copy manually
And if you want to pass a mutable reference to a variable, just use a regular lvalue reference:
void foo(std::string&);
Using rvalue references in this case is technically possible, but semantically improper and totally confusing.
The only, only place where an rvalue reference makes sense is in a move constructor or move assignment operator. In any other situation pass-by-value or lvalue references are usually the right choice and avoid a lot of confusion.
Note: do not confuse rvalue references with forwarding references that look exactly the same but work totally differently, as in:
template <class T>
void foo(T&& t) {
}
In the above example t looks like a rvalue reference parameter, but is actually a forwarding reference (because of the template type), which is an entirely different can of worms.

invalid initialization of non-const reference from an rvalue

So I have the following function:
void scan(std::istream& is, Handler& h);
I want to call it in different ways, like:
scan(std::cin, Handler());
scan(std::ifstream("myfile"), myhandler);
The compiler complains about std::ifstream("myfile") and Handler() of being rvalues being passed as non-const references, so the complaint is legitimate, but what can I do?
Neither function parameters cannot be const (istream is modified while read and the handler changes its state during callbacks).
If I change the parameter types to rvalue references (&&) then I will not be able to pass std::cin and sometimes I really care about the final state of myhandler thus I cannot apply std::move on them neither.
In principle I could make the parameters as universal references via template or auto&& type deduction and thus overload this function for all possible combinations of lvalue and rvalue references, but I have no intention of overloading this function for other types than I have already specified.
Are there any other options?
Somehow this whole move semantics got in the way in such a trivial example.
To convert an rvalue to an lvalue, you can use this lvalue helper function:
template<class T>
T& lvalue_ref(T&& x) { return x; }
And then the call becomes:
scan(lvalue_ref(std::ifstream("myfile")), lvalue_ref(Handler()));
This is safe as the temporaries (the ifstream and Handler) aren't destructed until the end of the full expression. However, note that these are lvalue references to temporaries and as such you must use caution when deciding to use this method. I'm assuming the scan() doesn't hold references/pointers to the arguments after it returns.
For example, do not use it like this:
int& x = lvalue_ref(5);
std::cout << x; // temporary is destructed, therefore Undefined Behavior
Just make sure the lifetime of the returned reference corresponds with the lifetime of the temporary, and you'll be fine.

std::move vs. compiler optimization

For example:
void f(T&& t); // probably making a copy of t
void g()
{
T t;
// do something with t
f(std::move(t));
// probably something else not using "t"
}
Is void f(T const& t) equivalent in this case because any good compiler will produce the same code? I'm interested in >= VC10 and >= GCC 4.6 if this matters.
EDIT:
Based on the answers, I'd like to elaborate the question a bit:
Comparing rvalue-reference and pass-by-value approaches, it's so easy to forgot to use std::move in pass-by-value. Can compiler still check that no more changes are made to the variable and eliminate an unnecessary copy?
rvalue-reference approach makes only optimized version "implicit", e.g. f(T()), and requires the user to explicitly specify other cases, like f(std::move(t)) or to explicitly make a copy f(T(t)); if the user isn't done with t instance. So, in this optimization-concerned light, is rvalue-reference approach considered good?
It's definitely not the same. For once T && can only bind to rvalues, while T const & can bind both to rvalues and to lvalues. Second, T const & does not permit any move optimizations. If you "probably want to make a copy of t", then T && allows you to actually make a move-copy of t, which is potentially more efficient.
Example:
void foo(std::string const & s) { std::string local(s); /* ... */ }
int main()
{
std::string a("hello");
foo(a);
}
In this code, the string buffer containing "hello" must exist twice, once in the body of main, and another time in the body of foo. By contrast, if you used rvalue references and std::move(a), the very same string buffer can be "moved around" and only needs to be allocated and populated one single time.
As #Alon points out, the right idiom is in fact passing-by-value:
void foo(std::string local) { /* same as above */ }
int main()
{
std::string a("hello");
foo(std::move(a));
}
Well, it depends what f does with t, if it creates a copy of it, then I would even go at length of doing this:
void f(T t) // probably making a copy of t
{
m_newT = std::move(t); // save it to a member or take the resources if it is a c'tor..
}
void g()
{
T t;
// do something with t
f(std::move(t));
// probably something else not using "t"
}
Then you allow the move c'tors optimization to happen, you take 't' resources in any case, and if it was 'moved' to your function, then you even gain the non copy of moving it to the function, and if it was not moved then you probably had to have one copy
Now if at later on in the code you'd have:
f(T());
Then ta da, free move optimization without the f user even knowing..
Note that quote: "is void f(T const& t) equivalent in this case because any good compiler will produce the same code?"
It is not equivelent, it is LESS work, because only the "pointer" is transferred and no c'tors are called at all, neither move nor anything else
Taking an const lvalue reference and taking an rvalue reference are two different things.
Similarities:
Neither will cause an copy or move to take place because they are both references. A reference just references an object, it doesn't copy/move it in any way.
Differences:
A const lvalue reference will bind to anything (lvalue or rvalue). An rvalue reference will only bind to non-const rvalues - much more limited.
The parameter inside the function cannot be modified when it is a const lvalue reference. It can be modified when it's an rvalue reference (since it is non-const).
Let's look at some examples:
Taking const lvalue reference: void f(const T& t);
Passing an lvalue:
T t; f(t);
Here, t is an lvalue expression because it's the name of the object. A const lvalue reference can bind to anything, so t will happily be passed by reference. Nothing is copied, nothing is moved.
Passing an rvalue:
f(T());
Here, T() is an rvalue expression because it creates a temporary object. Again, a const lvalue reference can bind to anything, so this is okay. Nothing is copied, nothing is moved.
In both of these cases, the t inside the function is a reference to the object passed in. It can't be modified by the reference is const.
Taking an rvalue reference: `void f(T&& t);
Passing an lvalue:
T t;
f(t);
This will give you a compiler error. An rvalue reference will not bind to an lvalue.
Passing an rvalue:
f(T());
This will be fine because an rvalue reference can bind to an rvalue. The reference t inside the function will refer to the temporary object created by T().
Now let's consider std::move. First things first: std::move doesn't actually move anything. The idea is that you give it an lvalue and it turns it into an rvalue. That's all it does. So now, if your f takes an rvalue reference, you could do:
T t;
f(std::move(t));
This works because, although t is an lvalue, std::move(t) is an rvalue. Now the rvalue reference can bind to it.
So why would you ever take an rvalue reference argument? In fact, you shouldn't need to do it very often, except for defining move constructors and assignment operators. Whenever you define a function that takes an rvalue reference, you almost certainly want to give a const lvalue reference overload. They should almost always come in pairs:
void f(const T&);
void f(T&&);
Why is this pair of functions useful? Well, the first will be called whenever you give it an lvalue (or a const rvalue) and the second will be called whenever you give it a modifiable rvalue. Receiving an rvalue usually means that you've been given a temporary object, which is great news because that means you can ravage its insides and perform optimizations based on the fact that you know it's not going to exist for much longer.
So having this pair of functions allows you to make an optimization when you know you're getting a temporary object.
There's a very common example of this pair of functions: the copy and move constructors. They are usually defined like so:
T::T(const T&); // Copy constructor
T::T(T&&); // Move constructor
So a move constructor is really just a copy constructor that is optimized for when receiving a temporary object.
Of course, the object being passed isn't always a temporary object. As we've shown above, you can use std::move to turn an lvalue into an rvalue. Then it appears to be a temporary object to the function. Using std::move basically says "I allow you to treat this object as a temporary object." Whether it actually gets moved from or not is irrelevant.
However, beyond writing copy constructors and move constructors, you'd better have a good reason for using this pair of functions. If you're writing a function that takes an object and will behave exactly the same with it regardless of whether its a temporary object or not, simply take that object by value! Consider:
void f(T t);
T t;
f(t);
f(T());
In the first call to f, we are passing an lvalue. That will be copied into the function. In the second call to f, we are passing an rvalue. That object will be moved into the function. See - we didn't even need to use rvalue references to cause the object to be moved efficiently. We just took it by value! Why? Because the constructor that is used to make the copy/move is chosen based on whether the expression is an lvalue or an rvalue. Just let the copy/move constructors do their job.
As to whether different argument types result in the same code - well that's a different question entirely. The compiler operates under the as-if rule. This simply means that as long as the program behaves as the standard dictates, the compiler can emit whatever code it likes. So the functions may emit the same code if they happen to do exactly the same thing. Or they may not. However, it's a bad sign if you're functions that take a const lvalue reference and an rvalue reference are doing the same thing.

Are unnamed objects and temporary objects equivalent?

In my effort to understand rvalue references, I have been pondering when the compiler will determine that a particular function argument is an rvalue reference, and when it will determine it to be an lvalue reference.
(This issue is related to reference collapsing; see Concise explanation of reference collapsing rules requested: (1) A& & -> A& , (2) A& && -> A& , (3) A&& & -> A& , and (4) A&& && -> A&&).
In particular, I have been considering if the compiler will always treat unnamed objects as rvalue references and/or if the compiler will always treat temporary objects as rvalue references.
In turn, this leads me to question whether unnamed objects are equivalent to temporary objects.
My question is: Are unnamed objects always temporary; and are temporary objects always unnamed?
In other words: Are unnamed objects and temporary objects equivalent?
I might be wrong, since I'm not sure what the definition of "unnamed object" is. But consider the argument of the foo() function below:
void foo(int)
{ /* ... */ }
int main()
{ foo(5); }
foo()'s argument is unnamed, but it's not a temporary. Therefore, unnamed objects and temporary objects are not equivalent.
Temporary objects can be named.
Very common case - when passed as a parameter to a function.
Another less common case - binding a const reference to an rvalue result of a function.
int f(int i) { return i + 1; }
int g() { const int &j = f(1); return j; }
Unnamed objects are often temporary, but not always. For example - anonymous union object:
struct S
{
union { int x; char y; };
} s;
And, of course, any object created by operator new.
Perhaps there are other cases, but even only these can serve as counterexamples to the hypothesis :)
I have been pondering when the compiler will determine that a particular function argument is an rvalue reference, and when it will determine it to be an lvalue reference.
I assume you are talking about function templates with universal reference parameters, like this?
template<typename T>
void foo(T&& t)
{
}
The rules are very simple. If the argument is an rvalue of type X, then T will be deduced to be X, hence T&& means X&&. If the argument is an lvalue of type X, then T will be deduced to be X&, hence T&& means X& &&, which is collapsed into X&.
If you were really asking about arguments, then the question does not make much sense, because arguments are never lvalue references or rvalue references, because an expression of type X& is immediately converted to an expression of type X, which denotes the referenced object.
But if you actually meant "How does the compiler distinguish lvalue arguments from rvalue arguments?" (note the missing reference), then the answer is simple: the compiler knows the value category of every expression, because the standard specifies for every conceivable expression what its value category is. For example, the call of a function is an expression that can belong to one of three value categories:
X foo(); // the expression foo() is a prvalue
X& bar(); // the expression bar() is an lvalue
X&& baz(); // the expression baz() is an xvalue
(Provided, of course, that X itself is not a reference type.)
If none of this answers your question, please clarify the question. Also, somewhat relevant FAQ answer.