0

When compiling the code below, I am getting an error on line 3 about the result of const_cast not being an lvalue. Is this only a problem because I used gcc 7.x (even though it is supposed to be fully C++17 compliant)? Or is this indeed invalid code according to the standard?

The code below is a minimal example that triggers the error. Tried gcc 7.1, 7.4, and also https://www.onlinegdb.com/online_c++_compiler and got the same error.

char* const a = "xyz";
char* b;
const_cast<char*>(a) = b;  // not lvalue error

The precise error gcc gives is: "error: lvalue required as left operand of assignment".

NOTE (forgot to add): the example has nothing to do with actual code I would ever write. It is an example I came about which (I presume) was created to test how well people understand the standard. So I am only interested in precisely what I asked in the question, i.e., whether this is valid code or not (and why). Thx!

chris
  • 60,560
  • 13
  • 143
  • 205
  • I get an error on this line `char* const a = "xyz";` check your error/warning levels live: https://godbolt.org/z/ZjJWPG – Richard Critten Oct 31 '19 at 18:52
  • 3
    There are very very very few cases where `const_cast` is needed. Until you hit "expert level" with the language you should avoid it. – NathanOliver Oct 31 '19 at 18:52
  • When you mark `a` as `const`, the compiler is free to put it in read-only memory. Even if it doesn't, it can assume you never modify `a` because it's `const`. Even if you work around the specific compiler error, this won't work the way you want. – chris Oct 31 '19 at 18:52
  • 2
    Be *Very*, Very, very careful with `const_cast`. If the object you are casting `const` off was not originally declared as non-const, then modifying it is Undefined Behaviour and your program is meaningless and the compiler may do *anything*. `const_cast` is an *extremely* sharp/dangerous tool that is very hard to use correctly and is usually best avoided entirely. – Jesper Juhl Oct 31 '19 at 18:53
  • @user12304836, I added the language-lawyer tag per your comment/update. – chris Oct 31 '19 at 18:57
  • Re your __update-Note__: as the example fails on the 1st line (error/warning) the next 2 lines are irrelevant as they depend on the 1st line being legal. – Richard Critten Oct 31 '19 at 18:58
  • 2
    @user12304836 " I am only interested in precisely what I asked in the question, i.e., whether this is valid code or not " - Well, in your example, `a` is declared as `const`, so modifying it is illegal - even after a `const_cast`. The only situation where modifying an object after casting away `const` is legal is when the object was originally declared as *non*-`const` but you received a `const` reference or pointer to it. In that case it *is* valid (although very bad style) to cast away `const` and modify the object. – Jesper Juhl Oct 31 '19 at 19:01

4 Answers4

4

So I am only interested in precisely what I asked in the question, i.e., whether this is valid code or not

It's not. The result of const_cast is a glvalue (lvalue or xvalue) only when casting to a reference type.

[expr.const.cast] (emphasis mine)

1 The result of the expression const_­cast<T>(v) is of type T. If T is an lvalue reference to object type, the result is an lvalue; if T is an rvalue reference to object type, the result is an xvalue; otherwise, the result is a prvalue and the lvalue-to-rvalue, array-to-pointer, and function-to-pointer standard conversions are performed on the expression v. Conversions that can be performed explicitly using const_­cast are listed below. No other conversion shall be performed explicitly using const_­cast.

You don't cast to a reference type, so the result is a prvalue; not something you may assign to. And don't go casting to a reference type either; attempting to modify an object declared as const gives undefined behavior. Your program will be another sort of invalid then.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
3

First, char* const a = "xyz"; is illegal. a string literal has the type const char[N] and assign it to a char * const removes the constness of the characters which is illegal in an implicit cast.

Now lets pretend that it's fine and lets look at

const_cast<char*>(a) = b

This has two issues. The first is that const_cast<char*>(a) results in a rvalue. For non-class types you cannot assign to rvalues. You would need const_cast<char*&>(a) in order to have an lvalue to assign to, and that brings up the next problem. You can't assign to an object that is const. Stripping away the const using const_cast doesn't fix the issue. It is still not allowed per [dcl.type.cv]/4

Any attempt to modify ([expr.ass], [expr.post.incr], [expr.pre.incr]) a const object ([basic.type.qualifier]) during its lifetime ([basic.life]) results in undefined behavior.

Even with the proper cast, the underlying object is still const so you violate the above clause and have undefined behavior.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • Wow, I am amazed by how quickly people chimed in with answers and clarified the issue for me. Many thanks to everyone, especially NathanOliver, StoryTeller, and Jesper Juhl for detailed explanations. [Am I supposed to close the question or mark an answer the best one??] – user12304836 Oct 31 '19 at 19:36
  • @user12304836 If you want you pick the answer you like the most and click on the check mark for it. That awards you and the answerer bonus reputation. The question doesn't need to be closed. Also, you're welcome. – NathanOliver Oct 31 '19 at 19:40
  • Thanks! It was a hard call, eventually I picked StoryTeller's. – user12304836 Oct 31 '19 at 19:55
0

The type char * const a defines a pointer variable a, which cannot be changed, but points to characters that can be changed. This is not a common use to make the pointer constant.

The error is telling you that you cannot update the value of a - it's not an lvalue, and I don't believe that const_cast gets around that in this case.

Could you possibly mean const char *a, which allows the pointer itself to be changed, but not the things pointed to?

Steve Friedl
  • 3,929
  • 1
  • 23
  • 30
0

An "lvalue" is a syntactic construct, meaning a kind of expression that can appear on the left of an assignment. You can assign to a variable, or an array component, or a field, but it's a syntax error to write an assignment to other kinds of expression such as x + y = 7; or f(x) = 5;. A function call such as const_cast<char*>(a) is not a kind of expression which can be assigned to.

It would be syntactically valid to write a = const_cast<char*>(b);, where the function call appears on the right of the assignment.

kaya3
  • 47,440
  • 4
  • 68
  • 97
  • 2
    `f(x) = 5;` is perfectly fine if `f(x)` returns a reference e.g. `int& f(int);`. ;-) – Scheff's Cat Oct 31 '19 at 19:00
  • 4
    This is only half true. There are plenty of instances where you can assign to the return of a function. In fact, `const_cast(a)` would get rid of the error (code is still broken though) – NathanOliver Oct 31 '19 at 19:00