This question already has answers here:
Function returning struct as LValue
(4 answers)
Closed 5 years ago.
Consider the following code:
#include <iostream>
using namespace std;
class X
{
int i;
public:
X(int ii = 0);
};
X::X(int ii) { i = ii; }
int a;
X f1() { return X(); }
int f2() { return a; }
int main() {
f1() = X(1);
f2() = 3;
}
If you try to run it, you get
error: lvalue required as left operand of assignment
on line 17, therefore
f1()
is considered a lvalue, while
f2()
is not. An explanation would be of great help of how things work would be of great help.
f1() is considered a lvalue
No, what f1 returns is still an rvalue (same as f2; more precisely it's a prvalue). But for class type, f1() = X(1); is just interpreted as f1().operator=(X(1));, which is pretty fine even though it might not make much sense; the temporary object returned by f1() will be destroyed soon. In short, you could call member functions on an rvalue with class type.
On the other hand, the similar behavior for built-in type is forbidden directly; assignment to such temporary doesn't make sense at all. That's why the compiler complains that it's not an lvalue.
f1() returns an rvalue. You have to return a reference (lvalue) to allow you to do this assignment
Change
int f2() { return a; }
to
int& f2() { return a; }
^
f1() returns rvalue but as instance of class f1() = X(1); calls assignment operator of class f1().operator=(X(1)); which is alright.
Read more about value categories here.
Related
[at line 14th where f().i = 10,the Xcode said Expression is not assignable,why?Is it illegal?]
class A{
public:
int i;
A():i(0){}
};
A f()
{
A a;
return a;
}
int main() {
f().i = 10;
return 0;
}
f() produces a temporary. C++ doesn't allow assigning to a member of a temporary. Formally, f().i is an xvalue, while assignment requires an lvalue.
Such an assignment would be rather pointless anyway, as the temporary would get destroyed very soon afterwards.
In this example here at the bottom, there are exemplary l-values defined:
// lvalues:
int& foo();
foo() = 42; // ok, foo() is an lvalue
int* p1 = &foo(); // ok, foo() is an lvalue
I am not shure what foo() is here? At first sight it looks like a function/method?
Is int& foo() the same as int& foo; ?
But on the other hand, my compiler says
Error: 'foo' declared as reference but not initialized int & foo;
Same with the rvalues foobar():
// rvalues:
int foobar();
int j = 0;
j = foobar(); // ok, foobar() is an rvalue
int* p2 = &foobar(); // error, cannot take the address of an rvalue
Yes, it is "a function/method", returning a reference to an int. Or, more precisely, this is a declaration of such a function, not definition: it says that such function exists, but does not provide the actual code (because the code is not relevant for the example). Compare how you define functions in header files.
A possible example of code for a similar function:
int a, b;
int& foo(bool which) {
if (which) return a;
else return b;
}
...
foo(true) = 10; // works as a = 10;
Yes, foo is indeed a function. The example is just trying to say "if a function returns an lvalue reference, then the result of calling it is an lvalue."
They probably just mean to say "there is a function foo returning an int& defined." Although technically, the code is correct as-is: it is possible in C++ to declare functions locally:
int main()
{
int foo();
return foo();
}
int foo()
{
return 42;
}
The example is referring to the value category of the function call expression foo(). In this case, foo() is an lvalue because foo returns an int&.
I found in a C++ book the following:
Although we will not be doing it in this book, you can overload a
function name (or operator) so that it behaves differently when used
as an l-value and when it is used as an r-value. (Recall that an
l-value means it can be used on the left-hand side of an assignment
statement.) For example, if you want a function f to behave
differently depending on whether it is used as an l-value or an
r-value, you can do so as follows:
class SomeClass {
public:
int& f(); // will be used in any l-value invocation const
const int& f( ) const; // used in any r-value invocation ...
};
I tried this and it didn't work:
class Foo {
public:
int& id(int& a);
const int& id(int& a) const;
};
int main() {
int a;
Foo f;
f.id(a) = 2;
a = f.id(a);
cout << f.id(a) << endl;
}
int& Foo :: id(int& a) {
cout << "Bar\n";
return a;
}
const int& Foo :: id(int& a) const {
cout << "No bar !\n";
return a;
}
Have I wrongly understood it ?
Either the book's example is flat-out wrong, or you copied the wrong example from the book.
class SomeClass {
public:
int& f(); // will be used in any l-value invocation const
const int& f( ) const; // used in any r-value invocation ...
};
With this code, when you call s.f() where s is an object of type SomeClass, the first version will be called when s is non-const, and the second version will be called when s is const. Value category has nothing to do with it.
Ref-qualification looks like this:
#include <iostream>
class SomeClass {
public:
int f() & { std::cout << "lvalue\n"; }
int f() && { std::cout << "rvalue\n"; }
};
int main() {
SomeClass s; s.f(); // prints "lvalue"
SomeClass{}.f(); // prints "rvalue"
}
Ofcourse the book is correct. Let me explain the workings of an example of what the author meant :
#include <iostream>
using namespace std;
class CO
{
int _m;
public:
CO(int m) : _m(m) {}
int& m() { return _m; } // used as an l-value
int const& m() const { return _m; } // used as an r-value
};
int main()
{
CO a(1);
cout << a.m() << endl;
a.m() = 2; // here used as an l-value / overload resolution selects the correct one
cout << a.m() << endl;
return 0;
}
Output is
1
2
What you misunderstood is the function signature. You see when you have an argument &arg (as in id(&arg)) you pretty much predefine the l-valuness of it, so returning it through a const or non const member function does not change a thing.
The author refers to a common writting style that allows for 'getters' and 'setters' to be declared with a signature different only in const qualifires yet compile and behave correctly.
Edit
To be more pedantic, the following phrase
Recall that an l-value means it can be used on the left-hand side of an assignment statement.
is not valid anymore. lr valuness applies to expressions, and the shortest way to explain it, is that an expression whose adress we can take, is an l-value; if it's not obtainable it's an r-value.
So the syntax to which the author refers to, enforces the member function to be used correctly (correct compilation / overload resolution) at both sides of the assignment operator. This nowdays is no longer relevant to lr valueness.
A const member function can only be called on a const object. It makes no difference what you do with the return value. In your example, f is non-const, so it always calls the non-const version of f(). Note that you can also overload on r-value references (&&) in C++11.
struct A {};
A f1()
{
return A();
}
int f2()
{
return int();
}
int main()
{
f1() = A(); // OK
f2() = int(); // error C2106: '=' : left operand must be l-value
}
Why is f1() = A(); OK while f2() = int(); is failed?
f1() returns an instance of A. Since you haven't overwritten the copy/move-assignment operator, the compiler generates one for you. You're essentially calling a member function:
f1() = A(); // calls A& operator=(A&&)
The second doesn't work because int is not of class type.
The function f1 returns a rvalue, which might become an xvalue (an “eXpiring” value). The function f2 returns a builtin type which is an rvalue becomming an prvalue (“pure” rvalue).
From 3.10 [Lvalues and rvalues]
— An xvalue (an “eXpiring” value) also refers to an object, usually
near the end of its lifetime (so that its resources may be moved, for
example). An xvalue is the result of certain kinds of expressions
involving rvalue references (8.3.2).
Hence, due to an implicit move operation the assignment of A becomes valid.
Changing A to:
struct A {
A() {}
A(A&&) = delete;
A& operator = (const A&) { return *this; }
};
produces: error: use of deleted function ‘A::A(A&&)’ with g++ (Ubuntu/Linaro 4.7.2-2ubuntu1) 4.7.2
struct B
{
};
struct A
{
operator A&() const;
operator B&() const;
};
int main()
{
const A a;
B& br = a;
A& ar = a;
}
Why can I create cast operator to B&, but not to A&.
May be it does not have much sense (one can use it to erase const modifier, as in example), but it at least inconsistent!
You can't do this because it's explicitly forbidden. N3290 § 12.3.2 states:
Such functions are called
conversion functions. No return type can be specified. If a conversion function is a member function, the
type of the conversion function (8.3.5) is “function taking no parameter returning conversion-type-id”. A
conversion function is never used to convert a (possibly cv-qualified) object to the (possibly cv-qualified)
same object type (or a reference to it), to a (possibly cv-qualified) base class of that type (or a reference to
it), or to (possibly cv-qualified) void.
(Emphasis mine)
This is discussed further in a note:
These conversions are considered as standard conversions for the purposes of overload resolution (13.3.3.1, 13.3.3.1.4) and
therefore initialization (8.5) and explicit casts (5.2.9).
Which explains this decision - it would interfere with the built-in mechanics too much. (For little gain).
If you really want something non-const from a const object the only smart way to do this is constructing a new instance using the copy constructor.
As a work around you could introduce a lightweight intermediary (like a smart pointer):
struct B {};
struct A {};
namespace {
B b_inst;
A a_inst;
}
struct A_wrapper {
A& inst;
// This is perfectly fine: const alters the reference, not what it refers to
operator A&() const { return inst; }
operator B&() const { return b_inst; }
A_wrapper() : inst(a_inst) {}
};
int main() {
const A_wrapper a;
B& br = a;
A& ar = a;
}
But really, wanting to do this in the first place looks like a code smell.
The proper way to do this would be to use const_cast.
For example,
#include <iostream>
using namespace std;
void f(int* p) {
cout << *p << endl;
}
int main(void) {
const int a = 10;
const int* b = &a;
// Function f() expects int*, not const int*
// f(b);
int* c = const_cast<int*>(b);
f(c);
// Lvalue is const
// *b = 20;
// Undefined behavior
// *c = 30;
int a1 = 40;
const int* b1 = &a1;
int* c1 = const_cast<int*>(b1);
// Integer a1, the object referred to by c1, has
// not been declared const
*c1 = 50;
return 0;
}
Declaring a conversion to a reference to self is not ill-formed. Your problem comes at the time where your reference is initialized. As the type of the reference and the type of the initialization expression are the same, the reference is bound directly and your user defined conversion operator is never considered. Thus normal conversion rules apply and const conversion makes the code ill-formed.
Anyway, what your are doing is basically asking yourself to get shot in the foot. If you don't like constness, don't use it. If you do it consistently, it will never bother you, but it is not going to make you new friends.