Reference Error Invalid Assignment Left-Hand Side Array Definition

The terms lvalue and rvalue are not something one runs into often in C/C++ programming, but when one does, it's usually not immediately clear what they mean. The most common place to run into these terms are in compiler error & warning messages. For example, compiling the following with :

intfoo() {return2;} intmain() { foo() = 2; return0; }

You get:

test.c: In function 'main': test.c:8:5: error: lvalue required as left operand of assignment

True, this code is somewhat perverse and not something you'd write, but the error message mentions lvalue, which is not a term one usually finds in C/C++ tutorials. Another example is compiling this code with :

int& foo() { return2; }

Now the error is:

testcpp.cpp: In function 'int& foo()': testcpp.cpp:5:12: error: invalid initialization of non-const reference of type 'int&' from an rvalue of type 'int'

Here again, the error mentions some mysterious rvalue. So what do lvalue and rvalue mean in C and C++? This is what I intend to explore in this article.

A simple definition

This section presents an intentionally simplified definition of lvalues and rvalues. The rest of the article will elaborate on this definition.

An lvalue (locator value) represents an object that occupies some identifiable location in memory (i.e. has an address).

rvalues are defined by exclusion, by saying that every expression is either an lvalue or an rvalue. Therefore, from the above definition of lvalue, an rvalue is an expression that does not represent an object occupying some identifiable location in memory.

Basic examples

The terms as defined above may appear vague, which is why it's important to see some simple examples right away.

Let's assume we have an integer variable defined and assigned to:

An assignment expects an lvalue as its left operand, and is an lvalue, because it is an object with an identifiable memory location. On the other hand, the following are invalid:

4 = var; // ERROR! (var + 1) = 4; // ERROR!

Neither the constant , nor the expression are lvalues (which makes them rvalues). They're not lvalues because both are temporary results of expressions, which don't have an identifiable memory location (i.e. they can just reside in some temporary register for the duration of the computation). Therefore, assigning to them makes no semantic sense - there's nowhere to assign to.

So it should now be clear what the error message in the first code snippet means. returns a temporary value which is an rvalue. Attempting to assign to it is an error, so when seeing the compiler complains that it expected to see an lvalue on the left-hand-side of the assignment statement.

Not all assignments to results of function calls are invalid, however. For example, C++ references make this possible:

int globalvar = 20; int& foo() { return globalvar; } int main() { foo() = 10; return0; }

Here returns a reference, which is an lvalue, so it can be assigned to. Actually, the ability of C++ to return lvalues from functions is important for implementing some overloaded operators. One common example is overloading the brackets operator in classes that implement some kind of lookup access. does this:

std::map<int, float> mymap; mymap[10] = 5.6;

The assignment works because the non-const overload of returns a reference that can be assigned to.

Modifiable lvalues

Initially when lvalues were defined for C, it literally meant "values suitable for left-hand-side of assignment". Later, however, when ISO C added the keyword, this definition had to be refined. After all:

constint a = 10; // 'a' is an lvalue a = 10; // but it can't be assigned!

So a further refinement had to be added. Not all lvalues can be assigned to. Those that can are called modifiable lvalues. Formally, the C99 standard defines modifiable lvalues as:

[...] an lvalue that does not have array type, does not have an incomplete type, does not have a const-qualified type, and if it is a structure or union, does not have any member (including, recursively, any member or element of all contained aggregates or unions) with a const-qualified type.

Conversions between lvalues and rvalues

Generally speaking, language constructs operating on object values require rvalues as arguments. For example, the binary addition operator takes two rvalues as arguments and returns an rvalue:

int a = 1; // a is an lvalueint b = 2; // b is an lvalueint c = a + b; // + needs rvalues, so a and b are converted to rvalues// and an rvalue is returned

As we've seen earlier, and are both lvalues. Therefore, in the third line, they undergo an implicit lvalue-to-rvalue conversion. All lvalues that aren't arrays, functions or of incomplete types can be converted thus to rvalues.

What about the other direction? Can rvalues be converted to lvalues? Of course not! This would violate the very nature of an lvalue according to its definition [1].

This doesn't mean that lvalues can't be produced from rvalues by more explicit means. For example, the unary (dereference) operator takes an rvalue argument but produces an lvalue as a result. Consider this valid code:

int arr[] = {1, 2}; int* p = &arr[0]; *(p + 1) = 10; // OK: p + 1 is an rvalue, but *(p + 1) is an lvalue

Conversely, the unary address-of operator takes an lvalue argument and produces an rvalue:

int var = 10; int* bad_addr = &(var + 1); // ERROR: lvalue required as unary '&' operandint* addr = &var; // OK: var is an lvalue &var = 40; // ERROR: lvalue required as left operand// of assignment

The ampersand plays another role in C++ - it allows to define reference types. These are called "lvalue references". Non-const lvalue references cannot be assigned rvalues, since that would require an invalid rvalue-to-lvalue conversion:

std::string& sref = std::string(); // ERROR: invalid initialization of// non-const reference of type// 'std::string&' from an rvalue of// type 'std::string'

Constant lvalue references can be assigned rvalues. Since they're constant, the value can't be modified through the reference and hence there's no problem of modifying an rvalue. This makes possible the very common C++ idiom of accepting values by constant references into functions, which avoids unnecessary copying and construction of temporary objects.

CV-qualified rvalues

If we read carefully the portion of the C++ standard discussing lvalue-to-rvalue conversions [2], we notice it says:

An lvalue (3.10) of a non-function, non-array type T can be converted to an rvalue. [...] If T is a non-class type, the type of the rvalue is the cv-unqualified version of T. Otherwise, the type of the rvalue is T.

What is this "cv-unqualified" thing? CV-qualifier is a term used to describe const and volatile type qualifiers.

From section 3.9.3:

Each type which is a cv-unqualified complete or incomplete object type or is void (3.9) has three corresponding cv-qualified versions of its type: a const-qualified version, a volatile-qualified version, and a const-volatile-qualified version. [...] The cv-qualified or cv-unqualified versions of a type are distinct types; however, they shall have the same representation and alignment requirements (3.9)

But what has this got to do with rvalues? Well, in C, rvalues never have cv-qualified types. Only lvalues do. In C++, on the other hand, class rvalues can have cv-qualified types, but built-in types (like ) can't. Consider this example:

#include <iostream>classA { public: void foo() const { std::cout << "A::foo() const\n"; } void foo() { std::cout << "A::foo()\n"; } }; A bar() { return A(); } const A cbar() { return A(); } int main() { bar().foo(); // calls foo cbar().foo(); // calls foo const }

The second call in actually calls the method of , because the type returned by is , which is distinct from . This is exactly what's meant by the last sentence in the quote mentioned earlier. Note also that the return value from is an rvalue. So this is an example of a cv-qualified rvalue in action.

Rvalue references (C++11)

Rvalue references and the related concept of move semantics is one of the most powerful new features the C++11 standard introduces to the language. A full discussion of the feature is way beyond the scope of this humble article [3], but I still want to provide a simple example, because I think it's a good place to demonstrate how an understanding of what lvalues and rvalues are aids our ability to reason about non-trivial language concepts.

I've just spent a good part of this article explaining that one of the main differences between lvalues and rvalues is that lvalues can be modified, and rvalues can't. Well, C++11 adds a crucial twist to this distinction, by allowing us to have references to rvalues and thus modify them, in some special circumstances.

As an example, consider a simplistic implementation of a dynamic "integer vector". I'm showing just the relevant methods here:

classIntvec { public: explicit Intvec(size_t num = 0) : m_size(num), m_data(newint[m_size]) { log("constructor"); } ~Intvec() { log("destructor"); if (m_data) { delete[] m_data; m_data = 0; } } Intvec(const Intvec& other) : m_size(other.m_size), m_data(newint[m_size]) { log("copy constructor"); for (size_t i = 0; i < m_size; ++i) m_data[i] = other.m_data[i]; } Intvec& operator=(const Intvec& other) { log("copy assignment operator"); Intvec tmp(other); std::swap(m_size, tmp.m_size); std::swap(m_data, tmp.m_data); return *this; } private: void log(constchar* msg) { cout << "[" << this << "] " << msg << "\n"; } size_t m_size; int* m_data; };

So, we have the usual constructor, destructor, copy constructor and copy assignment operator [4] defined, all using a logging function to let us know when they're actually called.

Let's run some simple code, which copies the contents of into :

Intvec v1(20); Intvec v2; cout << "assigning lvalue...\n"; v2 = v1; cout << "ended assigning lvalue...\n";

What this prints is:

assigning lvalue... [0x28fef8] copy assignment operator [0x28fec8] copy constructor [0x28fec8] destructor ended assigning lvalue...

Makes sense - this faithfully represents what's going on inside . But suppose that we want to assign some rvalue to :

cout << "assigning rvalue...\n"; v2 = Intvec(33); cout << "ended assigning rvalue...\n";

Although here I just assign a freshly constructed vector, it's just a demonstration of a more general case where some temporary rvalue is being built and then assigned to (this can happen for some function returning a vector, for example). What gets printed now is this:

assigning rvalue... [0x28ff08] constructor [0x28fef8] copy assignment operator [0x28fec8] copy constructor [0x28fec8] destructor [0x28ff08] destructor ended assigning rvalue...

Ouch, this looks like a lot of work. In particular, it has one extra pair of constructor/destructor calls to create and then destroy the temporary object. And this is a shame, because inside the copy assignment operator, another temporary copy is being created and destroyed. That's extra work, for nothing.

Well, no more. C++11 gives us rvalue references with which we can implement "move semantics", and in particular a "move assignment operator" [5]. Let's add another to :

Intvec& operator=(Intvec&& other) { log("move assignment operator"); std::swap(m_size, other.m_size); std::swap(m_data, other.m_data); return *this; }

The syntax is the new rvalue reference. It does exactly what it sounds it does - gives us a reference to an rvalue, which is going to be destroyed after the call. We can use this fact to just "steal" the internals of the rvalue - it won't need them anyway! This prints:

assigning rvalue... [0x28ff08] constructor [0x28fef8] move assignment operator [0x28ff08] destructor ended assigning rvalue...

What happens here is that our new move assignment operator is invoked since an rvalue gets assigned to . The constructor and destructor calls are still needed for the temporary object that's created by , but another temporary inside the assignment operator is no longer needed. The operator simply switches the rvalue's internal buffer with its own, arranging it so the rvalue's destructor will release our object's own buffer, which is no longer used. Neat.

I'll just mention once again that this example is only the tip of the iceberg on move semantics and rvalue references. As you can probably guess, it's a complex subject with a lot of special cases and gotchas to consider. My point here was to demonstrate a very interesting application of the difference between lvalues and rvalues in C++. The compiler obviously knows when some entity is an rvalue, and can arrange to invoke the correct constructor at compile time.

Conclusion

One can write a lot of C++ code without being concerned with the issue of rvalues vs. lvalues, dismissing them as weird compiler jargon in certain error messages. However, as this article aimed to show, getting a better grasp of this topic can aid in a deeper understanding of certain C++ code constructs, and make parts of the C++ spec and discussions between language experts more intelligible.

Also, in the new C++ spec this topic becomes even more important, because C++11's introduction of rvalue references and move semantics. To really grok this new feature of the language, a solid understanding of what rvalues and lvalues are becomes crucial.


JavaScript Errors and How to Fix Them

JavaScript can be a nightmare to debug: Some errors it gives can be very difficult to understand at first, and the line numbers given aren’t always helpful either. Wouldn’t it be useful to have a list where you could look to find out what they mean and how to fix them? Here you go!

Below is a list of the strange errors in JavaScript. Different browsers can give you different messages for the same error, so there are several different examples where applicable.

How to read errors?

Before the list, let’s quickly look at the structure of an error message. Understanding the structure helps understand the errors, and you’ll have less trouble if you run into any errors not listed here.

A typical error from Chrome looks like this:

Uncaught TypeError: undefined is not a function

The structure of the error is as follows:

  1. Uncaught TypeError: This part of the message is usually not very useful. Uncaught means the error was not caught in a statement, and is the error’s name.
  2. undefined is not a function: This is the message part. With error messages, you have to read them very literally. For example in this case it literally means that the code attempted to use like it was a function.

Other webkit-based browsers, like Safari, give errors in a similar format to Chrome. Errors from Firefox are similar, but do not always include the first part, and recent versions of Internet Explorer also give simpler errors than Chrome – but in this case, simpler does not always mean better.

Now onto the actual errors.

Uncaught TypeError: undefined is not a function

Related errors: number is not a function, object is not a function, string is not a function, Unhandled Error: ‘foo’ is not a function, Function Expected

Occurs when attempting to call a value like a function, where the value is not a function. For example:

var foo = undefined; foo();

This error typically occurs if you are trying to call a function in an object, but you typed the name wrong.

var x = document.getElementByID('foo');

Since object properties that don’t exist are by default, the above would result in this error.

The other variations such as “number is not a function” occur when attempting to call a number like it was a function.

How to fix this error: Ensure the function name is correct. With this error, the line number will usually point at the correct location.

Uncaught ReferenceError: Invalid left-hand side in assignment

Related errors: Uncaught exception: ReferenceError: Cannot assign to ‘functionCall()’, Uncaught exception: ReferenceError: Cannot assign to ‘this’

Caused by attempting to assign a value to something that cannot be assigned to.

The most common example of this error is with if-clauses:

if(doSomething() = 'somevalue')

In this example, the programmer accidentally used a single equals instead of two. The message “left-hand side in assignment” is referring to the part on the left side of the equals sign, so like you can see in the above example, the left-hand side contains something you can’t assign to, leading to the error.

How to fix this error: Make sure you’re not attempting to assign values to function results or to the keyword.

Uncaught TypeError: Converting circular structure to JSON

Related errors: Uncaught exception: TypeError: JSON.stringify: Not an acyclic Object, TypeError: cyclic object value, Circular reference in value argument not supported

Always caused by a circular reference in an object, which is then passed into .

var a = { }; var b = { a: a }; a.b = b; JSON.stringify(a);

Because both and in the above example have a reference to each other, the resulting object cannot be converted into JSON.

How to fix this error: Remove circular references like in the example from any objects you want to convert into JSON.

Unexpected token ;

Related errors: Expected ), missing ) after argument list

The JavaScript interpreter expected something, but it wasn’t there. Typically caused by mismatched parentheses or brackets.

The token in this error can vary – it might say “Unexpected token ]” or “Expected {” etc.

How to fix this error: Sometimes the line number with this error doesn’t point to the correct place, making it difficult to fix.

  • An error with [ ] { } ( ) is usually caused by a mismatching pair. Check that all your parentheses and brackets have a matching pair. In this case, line number will often point to something else than the problem character
  • Unexpected / is related to regular expressions. The line number for this will usually be correct.
  • Unexpected ; is usually caused by having a ; inside an object or array literal, or within the argument list of a function call. The line number will usually be correct for this case as well

Uncaught SyntaxError: Unexpected token ILLEGAL

Related errors: Unterminated String Literal, Invalid Line Terminator

A string literal is missing the closing quote.

How to fix this error: Ensure all strings have the correct closing quote.

Uncaught TypeError: Cannot read property ‘foo’ of null, Uncaught TypeError: Cannot read property ‘foo’ of undefined

Related errors: TypeError: someVal is null, Unable to get property ‘foo’ of undefined or null reference

Attempting to read or as if it was an object. For example:

var someVal = null; console.log(someVal.foo);

How to fix this error: Usually caused by typos. Check that the variables used near the line number pointed by the error are correctly named.

Uncaught TypeError: Cannot set property ‘foo’ of null, Uncaught TypeError: Cannot set property ‘foo’ of undefined

Related errors: TypeError: someVal is undefined, Unable to set property ‘foo’ of undefined or null reference

Attempting to write or as if it was an object. For example:

var someVal = null; someVal.foo = 1;

How to fix this error: This too is usually caused by typos. Check the variable names near the line the error points to.

Uncaught RangeError: Maximum call stack size exceeded

Related errors: Uncaught exception: RangeError: Maximum recursion depth exceeded, too much recursion, Stack overflow

Usually caused by a bug in program logic, causing infinite recursive function calls.

How to fix this error: Check recursive functions for bugs that could cause them to keep recursing forever.

Uncaught URIError: URI malformed

Related errors: URIError: malformed URI sequence

Caused by an invalid decodeURIComponent call.

How to fix this error: Check that the call at the error’s line number gets correct input.

XMLHttpRequest cannot load http://some/url/. No ‘Access-Control-Allow-Origin’ header is present on the requested resource

Related errors: Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://some/url/

This error is always caused by the usage of XMLHttpRequest.

How to fix this error: Ensure the request URL is correct and it respects the same-origin policy. A good way to find the offending code is to look at the URL in the error message and find it from your code.

InvalidStateError: An attempt was made to use an object that is not, or is no longer, usable

Related errors: InvalidStateError, DOMException code 11

Means the code called a function that you should not call at the current state. Occurs usually with , when attempting to call functions on it before it’s ready.

var xhr = new XMLHttpRequest(); xhr.setRequestHeader('Some-Header', 'val');

In this case, you would get the error because the function can only be called after calling .

How to fix this error: Look at the code on the line pointed by the error and make sure it runs at the correct time, or add any necessary calls before it (such as )

Conclusion

JavaScript has some of the most unhelpful errors I’ve seen, with the exception of the notorious in PHP. With more familiarity the errors start to make more sense. Modern browsers also help, as they no longer give the completely useless errors they used to.

What’s the most confusing error you’ve seen? Share the frustration in the comments!

Want to learn more about these errors and how to prevent them? Detect Problems in JavaScript Automatically with ESLint.

About Jani Hartikainen

Jani Hartikainen has spent over 10 years building web applications. His clients include companies like Nokia and hot super secret startups. When not programming or playing games, Jani writes about JavaScript and high quality code on his site.

codeutopia.netjhartikainenPosts

Comments

Leave a Reply

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