Anyway this is part of a rpg text based game I'm making. It's very crudely made but it's pretty stable so far. By the way, Player and Opponent are classes which I've made with the functions. 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.">

Error Lvalue Required As Left Operand Of Assignment C++ Functions

I have been struggling with the concepts of lvalue and rvalue in C++ since forever. I think that now is the right time to understand them for good, as they are getting more and more important with the evolution of the language.

Once the meaning of lvalues and rvalues is grasped, you can dive deeper into advanced C++ features like move semantics and rvalue references (more on that in future articles).

Lvalues and rvalues: a friendly definition

Firts of all, let's keep our heads away from any formal definition. In C++ an lvalue is something that points to a specific memory location. On the other hand, a rvalue is something that doesn't point anywhere. In general, rvalues are temporary and short lived, while lvalues live a longer life since they exist as variables. It's also fun to think of lvalues as containers and rvalues as things contained in the containers. Without a container, they would expire.

Let me show you some examples right away.

Here is an rvalue; a number (technically a literal constant) has no specific memory address, except for some temporary register while the program is running. That number is assigned to , which is a variable. A variable has a specific memory location, so its an lvalue. C++ states that an assignment requires an lvalue as its left operand: this is perfectly legal.

Then with , which is an lvalue, you can do stuff like that:

Here I'm grabbing the the memory address of and putting it into , through the address-of operator . It takes an lvalue argument and produces an rvalue. This is another perfectly legal operation: on the left side of the assignment we have an lvalue (a variable), on the right side an rvalue produced by the address-of operator.

However, I can't do the following:

Yeah, that's obvious. But the technical reason is that , being a literal constant — so an rvalue, doesn't have a specific memory location. I am assigning to nowhere.

This is what GCC tells me if I run the program above:

He is damn right; the left operand of an assigment always require an lvalue, and in my program I'm using an rvalue ().

I can't do that too:

GCC says:

He is right again. The operator wants an lvalue in input, because only an lvalue has an address that can process.

Functions returning lvalues and rvalues

We know that the left operand of an assigment must be an lvalue. Hence a function like the following one will surely throw the error:

Crystal clear: returns an rvalue (the temporary number ), which cannot be a left operand of assignment. Now, what happens if a function returns an lvalue instead? Look closely at the following snippet:

It works because here returns a reference, unlike above. A reference is something that points to an existing memory location (the variable) thus is an lvalue, so it can be assigned to. Whatch out for here: it's not the address-of operator, it defines the type of what's returned (a reference).

The ability to return lvalues from functions looks pretty obscure, yet it is useful when you are doing advanced stuff like implementing some overloaded operators. More on that in future chapters.

Lvalue to rvalue conversion

An lvalue may get converted to an rvalue: that's something perfectly legit and it happens quite often. Let's think of the addition operator for example. According to the C++ specifications, it takes two rvalues as arguments and returns an rvalue.

Let's look at the following snippet:

Wait a minute: and are lvalues, but the addition operator wants rvalues: how come? The answer is quite simple: and have undergone an implicit lvalue-to-rvalue conversion. Many other operators perform such conversion — subtraction, addition and division to name a few.

Lvalue references

What about the opposite? Can an rvalue be converted to lvalue? Nope. It's not a technical limitation, though: it's the programming language that has been designed that way.

In C++, when you do stuff like

you are declarying as of type : a reference to . It's called an lvalue reference. Now you can happily change the value of through its reference .

We know that a reference must point to an existing object in a specific memory location, i.e. an lvalue. Here indeed exists, so the code runs flawlessly.

Now, what if I shortcut the whole thing and try to assign directly to my reference, without the object that holds it?

On the right side we have a temporary thing, an rvalue that needs to be stored somewhere in an lvalue.

On the left side we have the reference (an lvalue) that should point to an existing object. But being a numeric constant, i.e. without a specific memory address, i.e. an rvalue, the expression clashes with the very spirit of the reference.

If you think about it, that's the forbidden conversion from rvalue to lvalue. A volatile numeric constant (rvalue) should become an lvalue in order to be referenced to. If that would be allowed, you could alter the value of the numeric constant through its reference. Pretty meaningless, isn't it? Most importantly, what would the reference point to once the numeric value is gone?

The following snippet will fail for the very same reason:

I'm passing a temporary rvalue () to a function that takes a reference as argument. Invalid rvalue to lvalue conversion. There's a workaround: create a temporary variable where to store the rvalue and then pass it to the function (as in the commented out code). Quite inconvenient when you just want to pass a number to a function, isn't it?

Const lvalue reference to the rescue

That's what GCC would say about the last two code snippets:

GCC complains about the reference not being const, namely a constant. According to the language specifications, you are allowed to bind a const lvalue to an rvalue. So the following snippet works like a charm:

And of course also the following one:

The idea behind is quite straightforward. The literal constant is volatile and would expire in no time, so a reference to it is just meaningless. Let's make the reference itself a constant instead, so that the value it points to can't be modified. Now the problem of modifying an rvalue is solved for good. Again, that's not a technical limitation but a choice made by the C++ folks to avoid silly troubles.

This makes possible the very common C++ idiom of accepting values by constant references into functions, as I did in the previous snipped above, which avoids unnecessary copying and construction of temporary objects.

Under the hood the compiler creates an hidden variable for you (i.e. an lvalue) where to store the original literal constant, and then bounds that hidden variable to your reference. That's basically the same thing I did manually in a couple of snippets above. For example:

Now your reference points to something that exists for real (until it goes out of scope) and you can use it as usual, except for modifying the value it points to:

Conclusion

Understanding the meaning of lvalues and rvalues has given me the chance to figure out several of the C++'s inner workings. C++11 pushes the limits of rvalues even further, by introducing the concept of rvalue references and move semantics, where — surprise! — rvalues too are modifiable. I will restlessly dive into that minefield in one of my next articles.

Sources

Thomas Becker's Homepage - C++ Rvalue References Explained (link)
Eli Bendersky's website - Understanding lvalues and rvalues in C and C++ (link)
StackOverflow - Rvalue Reference is Treated as an Lvalue? (link)
StackOverflow - Const reference and lvalue (link)
CppReference.com - Reference declaration (link)

C++

Cookbook/Common issues

Eigen mostly achieves a pleasantly readable mathematical syntax, but quirks of the C++ language sometimes show through. This section lists some common gotchas you may encounter when using Eigen.

Structures containing Eigen types as members

Suppose you are using a fixed-size vectorizable Eigen type in one of your own structures, which you allocate dynamically:

Eigen ensures that is 128-bit aligned with respect to the start of . Stack-allocating an instance of will also respect the alignment. The problem comes when dynamically allocating an instance of ; the default is not required to allocate a 128-bit aligned block of data, so member may be misaligned.

To address this, Eigen provides a macro which overloads 's to Do The Right Thing:

Full explanation: http://eigen.tuxfamily.org/dox/StructHavingEigenMembers.html

Using Eigen types with STL containers

When using fixed-size vectorizable Eigen types in STL containers, you must use an aligned allocator. Eigen provides one:

Unfortunately the situation with is complicated by a defect in the current C++ language definition; Eigen provides a header specifically to make work with aligned types:

The macro will avoid some problems with the original version of and make your code forward-compatible with future versions of Eigen.

Full explanation: http://eigen.tuxfamily.org/dox/StlContainers.html

Syntax for calling member templates

Eigen's matrix types are class templates, allowing you to configure them on element type, storage order (e.g. row-major or col-major), or specify fixed sizes for one or both dimensions. Some member functions of these matrix types have template parameters of their own which the user must specify explicitly; these are member templates.

For example, Eigen's (inherited by ) has a useful member template , which returns a read-write view of a sub-block of a matrix:

If we know the dimensions of the block at compile time, specifying them as template parameters is a nice win; it avoids unnecessary dynamic memory allocations, and may allow Eigen to perform optimizations such as vectorization and loop unrolling. Unfortunately, calling a member template can be tricky:

In the second (templated) case, not only will it (correctly) fail to compile, gcc will misparse the expression so thoroughly that you get error messages along the lines of "warning: left-hand operand of comma has no effect", "error: lvalue required as left operand of assignment", "error: invalid operands of types '<unresolved overloaded function type>' and 'int' to binary 'operator<'".

What happened? In the second case, the matrix types are dependent on the template parameter . Dependent types require us to give the compiler a little extra help. We need to preface the name of the member template with the keyword :

Using other Eigen functions, below is another (perhaps cleaner) way to write the function body. Note that is not a member template.

Member functions of Eigen::MatrixBase which are member templates include , , , , , , , and . Many of these functions have overloads which are not member templates; those replace the template arguments with regular function arguments, which is more flexible but introduces runtime overhead.

Creating typedefs for Eigen types

Creating a typedef for a fully-defined Eigen type is easy:

But what if we want to parameterize our type over floats and doubles? Then we want something like:

Unfortunately C++ does not currently allow template typedefs (they will be in C++0x with different syntax). The standard workaround is to make a "meta-function" that returns the desired type through its member typedef :

This approach is still fairly simple, but introduces a bit of syntactic noise since the user has to remember to add . Another approach is to use inheritance:

This looks like what we want; unfortunately the derived class does not inherit the constructors or assignment operators of the base Eigen type, so will not inter-operate nicely with regular Eigen matrices. Here is a more complex definition of that fixes those issues:

1 classFoo 2 { 3 doublex; 4 Eigen::Vector2dv; 5 } 6 7 Foofoo; 8 Foo *foo_ptr = newFoo; 9
1 classFoo 2 { 3 doublex; 4 Eigen::Vector2dv; 5 public: 6 EIGEN_MAKE_ALIGNED_OPERATOR_NEW 7 } 8 9 Foofoo; 10 Foo *foo_ptr = newFoo; 11
1 USING_PART_OF_NAMESPACE_EIGEN 2 3 4 std::map<int, Vector4f> m; 5 6 std::map<int, Vector4f, std::less<int>, Eigen::aligned_allocator<Vector4f> > m; 7
1 #define EIGEN_USE_NEW_STDVECTOR 2 #include <Eigen/StdVector> 3 4 std::vector<Eigen::Vector4f, Eigen::aligned_allocator<Eigen::Vector4f> > v; 5
1 template<typenameT> 2 classFoo 3 { 4 voidbar(); 5 template<intN> voidbaz(); 6 }; 7
1 template<intBlockRows, intBlockCols> 2 ReturnTypeblock(intstartRow, intstartCol); 3
1 2 voidtransform(Eigen::Matrix<double,3,4>& m, 3 constEigen::Matrix<double,3,3> trans, 4 constEigen::Quaternion<double> qrot) 5 { 6 m.block<3,3>(0,0) = qrot.toRotationMatrix().transpose(); 7 m.block<3,1>(0,3) = -m.block<3,3>(0,0) * trans; 8 } 9 10 11 template<typenameT> 12 voidtransform(Eigen::Matrix<T,3,4>& m, 13 constEigen::Matrix<T,3,3> trans, 14 constEigen::Quaternion<T> qrot) 15 { 16 m.block<3,3>(0,0) = qrot.toRotationMatrix().transpose(); 17 m.block<3,1>(0,3) = -m.block<3,3>(0,0) * trans; 18 } 19
1 2 template<typenameT> 3 voidtransform(Eigen::Matrix<T,3,4>& m, 4 constEigen::Matrix<T,3,3> trans, 5 constEigen::Quaternion<T> qrot) 6 { 7 m.templateblock<3,3>(0,0) = qrot.toRotationMatrix().transpose(); 8 m.templateblock<3,1>(0,3) = -m.templateblock<3,3>(0,0) * trans; 9 } 10
1 m.templatecorner<3,3>(Eigen::TopLeft) = qrot.toRotationMatrix().transpose(); 2 m.col(3) = -m.templatecorner<3,3>(Eigen::TopLeft) * trans; 3
1 typedefEigen::Matrix<float,3,1> Point; 2 3 Pointpt; 4
1 2 template<typenameT> 3 typedefEigen::Matrix<T,3,1> Point; 4 5 Point<float> pt; 6
1 template<typenameT> 2 structPoint 3 { 4 typedefEigen::Matrix<T,3,1> type; 5 }; 6 7 Point<float>::typept; 8
1 2 template<typenameT> 3 classPoint : publicEigen::Matrix<T,3,1> {}; 4 5 Point<float> pt; 6
1 template<typenameT> 2 classPoint : publicEigen::Matrix<T,3,1> 3 { 4 typedefEigen::Matrix<T,3,1> BaseClass; 5 6 public: 7 8 Point(constT& x, constT& y, constT& z) 9 : BaseClass(x, y, z) 10 {} 11 12 13 template<typenameOtherDerived> 14 Point(constEigen::MatrixBase<OtherDerived>& other) 15 : BaseClass(other) 16 {} 17 18 19 usingBaseClass::operator=; 20 }; 21 22 23 Point<float> pt(1.0, 2.5, -3.1); 24 Eigen::Vector3fv = pt; 25 pt = v; 26 Point<float> pt2(v); 27
Categories: 1

0 Replies to “Error Lvalue Required As Left Operand Of Assignment C++ Functions”

Leave a comment

L'indirizzo email non verrà pubblicato. I campi obbligatori sono contrassegnati *