1 Zulkis

Non Lvalue In Assignment Error C++ Runtime

Many languages do choose the route of making assignment a statement rather than an expression, including Python:

and Golang:

Other languages don't have assignment, but rather scoped bindings, e.g. OCaml:

However, is an expression itself.

The advantage of allowing assignment is that we can directly check the return value of a function inside the conditional, e.g. in this Perl snippet:

Perl additionally scopes the declaration to that conditional only, which makes it very useful. It will also warn if you assign inside a conditional without declaring a new variable there – will warn, will not.

Making the assignment in another statement is usually sufficient, but can bring scoping problems:

Golang heavily relies on return values for error checking. It therefore allows a conditional to take an initialization statement:

Other languages use a type system to disallow non-boolean expressions inside a conditional:

Of course that fails when using a function that returns a boolean.

We now have seen different mechanisms to defend against accidental assignment:

  • Disallow assignment as an expression
  • Use static type checking
  • Assignment doesn't exist, we only have bindings
  • Allow an initialization statement, disallow assignment otherwise
  • Disallow assignment inside a conditional without declaration

I've ranked them in order of ascending preference – assignments inside expressions can be useful (and it's simple to circumvent Python's problems by having an explicit declaration syntax, and a different named argument syntax). But it's ok to disallow them, as there are many other options to the same effect.

Bug-free code is more important than terse code.

answered Feb 13 '14 at 12:48

There is a couple of things that make C++ unique among other contemporary mainstream programming languages. One of these things is value semantics. This feature is easily missed by programmers that come into C++ from heavy-OO world, especially that there is no language keyword associated with it. In this post I will try to describe what it is and how it can be useful.

What is a value? What is an object?

The concept of a value has been exhaustively described in Elements of Programming by Alexander Stepanov and Paul McJones. It is hard to define a value, but we can show the meaning by examples. In an algebraic expression of the form x ≤ y, x and y are values. We may not know what the value x is, but we know this value is fixed and will always be the same value, e.g. 2. On the other hand, in a piece of code , is not a value, because it may be 2 at one time, and 4 just a second later. is an object that can hold different values at different times. This definition of an object is different than the one from OO world, where object denotes something that has methods and is an instance of a class. In the context of C++ an object is a piece of memory that has an address, a type, and is capable of storing values. The same sequence of bits stored in two objects may be interpreted as different values if the two objects’ types differ; e.g., the sequence of bits can be interpreted as number 65 if object’s type is , or as character ‘A’ if object’s type is .

The distinction into values and objects is somewhat similar to the original distinction into r-values and l-values:

lval = rval;

An l-value, the one on the left-hand side of the assignment, needs to name an object because we will be changing the value the object stores. In the case of an r-value, the one on the right-hand side of the assignment, we only require that it names a value: we will not change it, we only need to copy it. As in this instruction:

x = 2;

We must be able to query for its address (because under this address we will store a new value), but we do not have to query the literal 2 for address.

Value semantics

Value semantics is the programming style, or the way of thinking, where we focus on values that are stored in the objects rather than objects themselves. The objects are only used to convey values: we do not care about object’s identity.

Complex mulAdd( Complex a, Complex b, Complex c ) { Complex ans = a * b + c; return ans; } Complex z = mulAdd(g, h, i);

Here, the computed value of a complex number was stored in automatic object , then the value was (or could be) transferred to a temporary, and then to another automatic object: . In fact, it might not have been this many objects because of optimizations known as copy elision; but this does not matter at all, because even though we need objects to convey values, we do not care about the objects, and how many of them there were, and where they were. We will be only be interested in the computed value.

In contrast to value semantics, sometimes we are very interested in the very object, and we want to operate exactly on this object, at exactly this address:

ostream & operator<<( ostream & out, Complex c ) { return out << c.real << ' ' << c.imag; }

Here, the stream may be a global which allocated resources necessary for writing text into terminal, and we need to return the reference to the very same object. This way of programming style can be called reference semantics. However, note that we passed by value, because in that case, again, it is only value that matters. In C++ we can mix value and reference semantics.

Why we use value semantics

First, you get value semantics by default. When declaring function arguments or return values, if you specify only the type name (like ) you get value semantics (you pass and return by value). If you wan to use reference semantics, you must make an extra effort to add a reference or pointer type symbol.

Second, we use value semantics in function declarations, because it closely follows the notation and reasoning from mathematics. In mathematics you operate on values. For instance, you define a function as follows:

    f: int × int → int
    f(x, y) = x·x + y·y

This is very similar to:

int f( int x, int y ) { return x * x + y * y; }

Or even a bit more similar with the new function notation:

auto f( int x, int y ) -> int { return x * x + y * y; }

Third, we do not run into any memory management issues. No dangling references to nonexistent objects, no expensive and unnecessary free store allocation, no memory leaks, no smart or dumb pointers. The support for value semantics in C++ — passing variables by value — eliminates all those problems.

Fourth, we avoid any reference aliasing problems. Andrew Koenig has neatly illustrated the problem of reference aliasing in this article. In multi-threaded environment passing by value and ensuring that each thread has its own copy of the value helps avoid any unnecessary data races. Then you do not need to synchronize on such values, and the program runs faster, and is safer because it avoids any deadlocks.

Fifth, for referential transparency. This means that you get no surprises where your data is modified behind the scenes without you being able to see that. For instance, consider this example from Dave Abrahams:

Matrix x = y; y[0][0] = 3.14; // (*)

The value of is not modified after the assignment in the second line. The objects are separated from one another. There is no connection between them anymore. This is not so for many languages that enforce OO programming model.

You can also observe referential transparency by comparing the following two ways of advancing an STL iterator:

i = next(i, 1); // (1) advance(i, 1); // (2)

Function does not modify anything. It just takes one iterator pointing to one element and returns another iterator that points to the next element. If you called it three times instead of one it wouldn’t change the behaviour of the program at all:

i = (next(i, 1), next(i, 1), next(i, 1));

In other words, does not have any side effects. In contrast, function changes the very object it is called with. If we called it three times instead of one, we would alter program behaviour by advancing the iterator further and further. Now, what if we wanted to advance the iterator by two? Forget for a moment that in each case you can pass 2 as the second argument to do that, and for the example’s sake let’s do it with 1s. In the value semantic approach we compose function calls:

i = next(next(i));

Using reference semantics and imperative approach, we repeat the commands:

advance(i, 1); advance(i, 1);

The value-semantic approach isn’t entirely pure (in functional programming sense) because in the end we do modify with an assignment. But it somehow separates the pure parts from impure ones.

Why we do not use value semantics

Whether we pass by value or by reference often depends on a programming style. For one instance, if you think in OO terms, where you want to create an object, and let it live for a significant amount of time, and want different parties to modify the object, you need to pass by reference. This is the case for globals like that need to be accessible from different places in the code, and it always has to be this very object at this very address. In other words if this is the address of the object that you are interested it, value semantics will not do — even if you are not operating on addresses explicitly.

Since we mention addresses, this is the right place to mention pointers here. They are very peculiar, because they are objects that store values. However, the values they store are addresses in memory which can hardly be used for anything else than referring to objects. Therefore at the higher level of abstraction passing and returning pointers to and from functions is usually considered passing by reference (reference semantics), even though technically you are passing values. But note that there is a limit to the analogy between pass-by-pointer and pass-by-reference, as nicely outlined in this article.

Next, the reason people often resort to allocating objects in free store (on the heap) and returning a (more or less) smart pointer to it, is that value semantics does not play well with OO techniques that require access to objects via references or pointers to enable virtual function calls. It is also not easy to pass polymorphic types by value, because we are risking slicing. There are techniques to overcome this difficulty, like type erasure, but they require some non-trivial effort to implement. They are used in , (for storing allocator). There are libraries that assist with implementing type erasure like, well, type_erasure or Adobe Poly Library.

One other common reason for choosing not to use value semantics is the fear for performance hit. Is this fear justified? The very short answer is: sometimes it is, sometimes it isn’t and sometimes it is avoiding value semantics that hits your performance.

Performance of value semantics

C++ encourages value semantics. The language is designed to make value semantics as useful and as fast as possible. Passing by value may be faster than you think. Let’s consider this example:

vector<string> getPoems() { vector<string> ans; // fill ans; return ans; } vector<string> poems = getPoems();

How many times does the copy constructor of get called? The answer is: not even once! This is because we do not require the two copies of the value — we only want to hand the value over. This is where the move constructor is chosen. Move constructor for a typical vector implementation costs six pointer assignments regardless of the types being stored or the vector’s size. So, let’s rephrase the question: how many times the move constructors gets called? In efficient compiler with all global optimizations enabled: most probably, not even once! This is owing to the compiler optimization technique known as copy elision.

Now, how about passing the arguments by value? First, let’s consider the situation where in the function implementation you need to make the copy of the argument. It is often the case when we implement the copy assignment and want to provide strong (commit-or-rollback) guarantee. In such cases passing by value, and not making the local copy is more efficient in general and equally efficient as passing by reference in the worst case. This is very well described by Dave Abrahams in this article.

In the other case, where only want to query the given value for some attributes, passing by reference is more efficient that passing by value for types with expensive copy constructors.

int remainingRoom( Dictionary const& d ) { return d.maxRoom() - d.size(); } void fun() { Dictionary d = buildDictionary(); // cheap by-value int remaining = remainingRoom(d); use(d); }

Here, if we passed the dictionary by value to function we would cause an unnecessary copying. While passing the arguments by a reference to const object eliminates the unnecessary copy, it should be kept in mind that it is passing by reference, and it does not mean that the object referred by will not change. I refer you again to the very important article by Andrew Koenig. So, could a similar copy elision not be applied in this case? C++ as it is specified today does not allow that, but there does not appears to be anything that shoul make such an optimization hard. It has been called ‘compile-time copy-on-write’ by Dave Abrahams. It is likely that we will find this optimization in C++ in the future.

Passing by value is not copying

While we were able to see that sometimes passing by value requires the call to copy constructor, in general this is not so. Have a look at this example.

unique_ptr<int> make1() { return unique_ptr<int>( new int(1) ); } unique_ptr<int> increment( unique_ptr<int> i ) { ++*i; return i; } int main() { unique_ptr<int> p = increment(make1()); }

Notice that function takes the argument and returns by value. Yet does not have a copy constructor. Do you find it surprising? It is not surprising if you adapt the C++ way of thinking: you tell the compiler that you are interested in values rather than objects, and the compiler chooses the best tool to implement your intentions. It has a couple of tools:

  1. Copy elision
  2. Move constructor
  3. Copy constructor
  4. The ‘as-if’ rule: it enables the compiler to do whatever it wants as long as you cannot tell the difference.

More…

Value semantics are a way of thinking about the program and computations. It cannot be comprehensively described in a short post. I just wanted to provide an overview of the idea. There is a lot more to be learned. Just to give you a couple of references:

  1. C++Next has a nice series of articles on value semantics.
  2. N2479 — this proposal tries to formally define the value.
  3. “Fundamentals of Generic Programming” provides a definition of a Regular Type, which is also a core of value semantics.

Like this:

LikeLoading...

Related

This entry was posted in programming and tagged C++11, value semantics. Bookmark the permalink.

Leave a Comment

(0 Comments)

Your email address will not be published. Required fields are marked *