As known, the function call which return type is an rvlaue to a function is an lvalue.
A function call is an lvalue if the result type is an lvalue reference
type or an rvalue reference to function type, an xvalue if the result
type is an rvalue reference to object type, and a prvalue otherwise.
#include <iostream>
int a(){ return 1; }
int foo(){ return 1; }
int (&&bar())(){ return a; }
int main()
{
bar() = foo; //error: cannot convert 'int()' to 'int()' in assignment
}
What's wrong with that diagnostic message?
Emphasis mine, [expr.ass]/1:
The assignment operator (=) and the compound assignment operators all group right-to-left. All require a
modifiable lvalue as their left operand and return an lvalue referring to the left operand...
[basic.lval]/6:
Functions cannot be modified, but pointers to functions can be modifiable.
So you may have an lvalue referring to a function but it is not a modifiable lvalue, and cannot be used to modify the function.
The diagnostic message... leaves something to be desired. Clang 3.6 says,
error: non-object type 'int ()' is not assignable
which is clearer.
Related
I have a bit confusion about this code:
struct A
{
A& bar()&&;
};
A& A::bar()&&
{
std::cout << "A::bar()&&\n";
return *this;
}
int main()
{
A{}.bar();// called by an rvalue
}
So what I understand is that bar can be called only by a modifiable-rvalue. Until this it is OK. But how can bar return a non-constant lvalue reference to that rvalue?
How bar() binds and returns a modifiable lvalue reference to that rvalue object?
The reason is that the this pointer for a class C can be either C* or const C* - not C& * or C&& * (those aren't actual types; you can't declare a C& * ptr). So, even when your method runs for an rvalue instance of class A, you get one of those two (GodBolt). And when you apply the * operator, you get an lvalue, not an rvalue.
This has to do with [expr.unary.op]/1
The unary * operator performs indirection: the expression to which it is applied shall be a pointer to an object type, or a pointer to a function type and the result is an lvalue referring to the object or function to which the expression points. If the type of the expression is “pointer to T”, the type of the result is “T”. [ Note: Indirection through a pointer to an incomplete type (other than cv void) is valid. The lvalue thus obtained can be used in limited ways (to initialize a reference, for example); this lvalue must not be converted to a prvalue, see [conv.lval]. — end note ]
emphasis mine
So when you dereference this yo get an lvalue. It doesn't matter if this is pointing to a temporary object or not, you will always get an lvalue. Since *this is an lvalue, you are legally allowed to return an lvalue reference, the program in syntactically correct. Semantically it is not, but that is a lot harder to test for and is often not something that is diagnosed as it requires quite a bit of static analysis.
It would be cool if the language could be updated where * only yields an lvalue when applied to this in a non-rvalue qualified function.
I saw an answer about lvalue yesterday, what below is its content:
— An lvalue (so called, historically, because lvalues could appear on
the left-hand side of an assignment expression) designates a function
or an object. [ Example: If E is an expression of pointer type, then
*E is an lvalue expression referring to the object or function to which E points. As another example, the result of calling a function
whose return type is an lvalue reference is an lvalue. —end example ]
so a question comes out: is reference itself a lvalue?
I know that both lvalue-reference and rvalue-reference can appear on the left-hand side of an assignment expression, but reference is neither function nor object, is there a contradiction? And Is reference really a lvalue?
Moreover, if a reference is a lvalue, what's the type of decltype(reference-expression), for example:
int main (void) { int a = 1; int &b = a; decltype((b)) c; }
what's the type of c?
In your code, the expression b is an lvalue, with type int.
The declaration of c is the same as int &c. (So it's ill-formed since you didn't provide an initializer). If you did provide an initializer, the expression c is an lvalue with type int.
I know the type of i below in main() is an int&. That's why it must be initialized.
int& f(){ static int i = 1; return i; }
int main()
{
decltype(f()) i = f();
}
But using paragraph 5p5 in the Standard, I conclude that the expression f() has type int as the reference is dropped.
From 7.1.6.2p4, how can one say that the expression f() is an lvalue, given that the reference was dropped from the function return?
The reference is dropped when evaluating the type of the expression (as per § 5/5), but this does not change the fact that the function call expression f() is an lvalue. Per paragraph 5.2.2/10 of the C++11 Standard:
A function call is an lvalue if the result type is an lvalue reference type or an rvalue reference to function
type, an xvalue if the result type is an rvalue reference to object type, and a prvalue otherwise.
In other words, the reference is not dropped from the return type of the function itself, only from the type of the evaluated function call expression (which is, therefore, int).
The fact that the function is returning an lvalue reference is what allows the type system to classify corresponding function call expressions as lvalues - which, in turn, allows decltype to add the lvalue reference to the type of the expression, thus yielding int&.
The code is as following:
struct A
{
static int k;
int i;
};
int A::k = 10;
A func() { A a; return a; }
My question is, how can I tell whether func().k or func().i is an lvalue or not? If both are lvalues/rvalues, how can I test them?
func().k = 0; // compile ok under g++-4.4 and g++-4.6
func().i = 1; // compile ok with g++-4.4, but g++-4.4 gives an error:
//"using temporary as lvalue [-fpermissive]"
func().k is an lvalue and func().i is an xvalue.
You can see this for more details:
rvalues and temporary objects in the FCD
Althrough, it is not difficult to test whether they are lvalues or rvalues:
#include <iostream>
struct A
{
static int k;
int i;
};
int A::k = 10;
A func( ){ A a; return a; }
void f (int & ) { std::cout << "int& " << std::endl; }
int main ()
{
func().k = 0; //ok, because func().k is an r
f(func().k);
func().i = 1; //compile error: "using temporary as lvalue"
f(func().i); //compile error because func().i is an rvalue of type ‘int’
return 0;
}
"Assignability" isn't a good test for lvalueness as we can have non-mutable lvalues (e.g. const reference expressions) and assignment may be a member function call which can be made on an rvalue of class type.
You have to refer to the standard (ISO/IEC 14882:2011).
5.2.2/10: A function call is an lvalue if the result type is an lvalue reference type or an rvalue reference to function type, an xvalue if the result type is an rvalue reference to object type, and a prvalue otherwise.
So func() is a prvalue.
5.2.5/4: If E2 is declared to have type "reference to T," then E1.E2 is an lvalue; the type of E1.E2 is T. Otherwise, one of the following rules applies.
If E2 is a static data member and the type of E2 is T, then E1.E2 is an lvalue; the expression designates the named member of the class. The type of E1.E2 is T`.
So func().k is an lvalue.
If E2 is a non-static data member and the type of E1 is "cq1 vq1 X", and the type of E2 is "cq2 vq2 T", the expression designates the named member of the object designated by the first expression. If E1 is an lvalue, then E1.E2 is an lvalue; if E1 is an xvalue, then E1.E2 is an xvalue; otherwise, it is a prvalue. [...]
So func().i is a prvalue.
There is no need to actually "test" whether a given value is an lvalue or an rvalue.
Etymologically/Historically, an lvalue is what is on the left side of the assigment operator, and an rvalue what is on the right side. (This definition is not exactly correct though.) Note that rvalues can be lvalues.
Simple rule of thumb: If you can take its address, it's an lvalue. However, in C++11 there are rvalue references which makes things not that simple. So the rule is more like: if you can take its address using &.
Maybe some examples will clear things up:
int a = 5; // a is an lvalue, 5 is an rvalue
int b = fun(); // fun is an rvalue
fun() = a; // illegal, can't assign to an rvalue (suppose foo returns int)
See http://en.wikipedia.org/wiki/Value_%28computer_science%29 for more.
I am not sure what exactly you are trying to achieve but as you are allocating memory within scope of function() returning that object. This object goes out of scope once the call to function() returns and hence it can be freed by the compiler. You can face a lot of troubles in later phases. I think you need to redesign the problem solution.
A function type (lvalue) can be converted to a pointer to function (rvalue).
int func();
int (*func_ptr)() = func;
But from (4.1/1)
An lvalue (3.10) of a non-function, non-array type T can be converted
to an rvalue.
Does it mean that a lvalue to rvalue conversion is not done on functions? Also, when an array decays to pointer doesn't it return a rvalue which is a pointer?
Functions are lvalues. A pointer to a function (a data type) can be
either; if you give it a name, it's an lvalue; otherwise, it's not
(roughly speaking). Pointers to functions obey all of the usual lvalue
to rvalue conversion rules. For simple types like the basic types or
pointers, the lvalue to rvalue conversion basically means reading the
variable.
void func(); // Declares func
(*(&func))(); // The expression &func is an rvalue
void (*pf)() = &func; // pf is an lvalue
(*pf)(); // In the expression *pf, pf undergoes an
// lvalue to rvalue conversion
Note that there is an implicit conversion of function to pointer to
function, and that the () operator works on both functions and
pointers to functions, so the last two lines could be written:
void (*pf)() = func;
pf();
As always, the result of the conversion is an rvalue (unless the
conversion is to a reference type). This is also the case when an array
is implicitly converted to a pointer; both arrays and functions can only
exist as lvalues, but they both implicitly convert to a pointer,
which is an rvalue. But that pointer can be used to initialize a
variable of the appropriate pointer type; such variables are lvalues.