Undefined behavior with const_cast

I was hoping someone could clarify what is meant by undefined behavior in C ++. Given the following class definition:

class Foo { public: explicit Foo(int Value): m_Int(Value) { } void SetValue(int Value) { m_Int = Value; } private: Foo(const Foo& rhs); const Foo& operator=(const Foo& rhs); private: int m_Int; }; 

If I correctly understood that two const_casts for both the link and the pointer in the following code will remove the constant of the original object of type Foo, but any attempts to make this object are modified using the pointer or the link will lead to undefined behavior.

 int main() { const Foo MyConstFoo(0); Foo& rFoo = const_cast<Foo&>(MyConstFoo); Foo* pFoo = const_cast<Foo*>(&MyConstFoo); //MyConstFoo.SetValue(1); //Error as MyConstFoo is const rFoo.SetValue(2); //Undefined behaviour pFoo->SetValue(3); //Undefined behaviour return 0; } 

What puzzles me is why this works, and it will change the original const object, but it won’t even warn me about warning that this behavior is undefined. I know that const_casts, in general, frowned, but I can imagine a case where a lack of awareness that a C-style cast could lead to the creation of const_cast might not happen if not noticed, for example:

 Foo& rAnotherFoo = (Foo&)MyConstFoo; Foo* pAnotherFoo = (Foo*)&MyConstFoo; rAnotherFoo->SetValue(4); pAnotherFoo->SetValue(5); 

In what circumstances can this behavior lead to a fatal runtime error? Is there any compiler option that I can configure to alert me of this (potentially) dangerous behavior?

NB: I am using MSVC2008.

+7
c ++ undefined-behavior const-cast
source share
7 answers

I was hoping someone could clarify what is meant by undefined behavior in C ++.

Technically, “Undefined Behavior” means that the language does not define the semantics for doing such a thing.

In practice, this usually means that it does not , it can break down when your compiler performs optimization or for other reasons. "

What puzzles me is why this works and will modify the original const object , but it does not even give me a warning that this behavior is undefined.

In this particular example, an attempt to modify any non-mutable object may "work" or may overwrite memory that does not belong to the program or belongs to [part] of some other object, since the non-mutable object can be optimized at compile time or can exist in some data segment only for reading in memory.

The factors that can lead to these things are simply too complicated to list. Consider the case of dereferencing an uninitialized pointer (also UB): the "object" you are working with will have some arbitrary memory address, which depends on what value occurred in the memory at the pointer's location; that the “value” potentially depends on previous program calls, previous work in the same program, storage of user input, etc. It just doesn't seem possible to try to rationalize the possible results of calling undefined Behavior, so again, we usually don’t worry and instead say “ don't do this ”.

Which puzzles me, why it works, and will change the original const object , but it won’t even offer me to warn that this message is undefined.

As an additional complication, compilers are not required to diagnose (issue warnings / errors) for undefined Behavior, because the code that causes undefined Behavior does not match the wrong code (i.e., it is obviously illegal), In many cases it is not advisable for the compiler to even detect UB, therefore, this is an area in which the responsibility of the programmer lies with the programmer.

Type of system: - including the existence and semantics of the const keyword - represents the basic protection against writing code that will break; A C ++ programmer should always remember that undermining this system — for example, by breaking const ness — is done at your own peril and risk, and this is usually a bad idea. ™

I can imagine a case where a lack of awareness that a C-style cast could lead to the execution of const_cast might not be noticed.

That's right. With warning levels set high enough, the common sense compiler can warn you about it, but it’s not necessary, and it may not happen. All in all, this is a good reason why C style castes are not approved, but they are still supported for backward compatibility with C. This is just one of those bad things.

+11
source share

Undefined behavior literally means only that: behavior that is not defined by the language standard. This usually happens in situations where the code is doing something wrong, but the error cannot be detected by the compiler. The only way to catch the error is to introduce a run-time test, which can hurt performance. Therefore, instead, the language specification tells you that you should not do certain things, and if so, something could happen.

In the case of writing to a permanent object, using const_cast to undermine compile-time checks, there are three likely scenarios:

  • it is treated as an inconsistent object, and its record changes it;
  • it is placed in a write-protected memory, and writing to it causes a protection error;
  • it is replaced (during optimization) by constant values ​​embedded in the compiled code, so after writing it, it will still have its initial value.

In your test, you ended up in the first scenario - the object was (almost certainly) created on the stack, which is not write-protected. You may find that you get a second script if the object is static, and a third if you enable more optimization.

In general, the compiler cannot diagnose this error - there is no way to say (with the exception of very simple examples like yours) whether the purpose of the link or pointer is constant or not. You have to make sure that you use only const_cast when you can guarantee that it is safe - either when the object is not constant, or when you are not going to change it at all.

+4
source share

What puzzles me, why does it work?

This is what undefined behavior means.
He can do everything, including seem to work.
If you increase the optimization level to its upper value, it will probably stop working.

but didn't even warn me, warning me that this behavior is undefined.

At the time it was a modification, the object is not const. In the general case, he cannot say that the object was originally const, so you cannot warn you. Even if each statement was evaluated independently, without reference to others (when considering this kind of warning generation).

Secondly, using the cast command, you tell the compiler "I know what I'm doing, overriding all your security functions and just doing it . "

For example, the following works just fine: (or will seem too (in the style of a nose deamon))

 float aFloat; int& anIntRef = (int&)aFloat; // I know what I am doing ignore the fact that this is sensable int* anIntPtr = (int*)&aFloat; anIntRef = 12; *anIntPtr = 13; 

I know that const_casts, in general, frowned

This is the wrong way to look at them. This is a way of documenting the code that you are doing something weird that should be tested by smart people (since the compiler will obey the cast without question). The reason you need a smart person to test is because it can lead to undefined behavior, but the good thing is that you have explicitly recorded it in your code right now (and people will definitely look carefully at what you have done).

but I can imagine a case where a lack of awareness that C-style casting can lead to the creation of const_cast may not occur if you do not notice, for example:

In C ++, there is no need to use the C style style. In the worst case, the C-Style cast can be replaced with reinterpret_cast <> but when porting the code you want to see if you could use static_cast <>. The point of C ++ is to make them stand out so that you can see them and at a glance determine the difference between dangerous throws of benign throws.

+4
source share

Undefined behavior depends on how the object was created , you can see Stephan explaining this at about 00:10:00, but essentially execute the following code:

 void f(int const &arg) { int &danger( const_cast<int&>(arg); danger = 23; // When is this UB? } 

Now there are two cases for calling f

 int K(1); f(k); // OK const int AK(1); f(AK); // triggers undefined behaviour 

To summarize, K was not born const, therefore, when you press f, accompaniment occurs, while AK was born a const , therefore ... UB is.

+3
source share

A classic example is an attempt to modify the string literal const that may exist in a protected data segment.

+2
source share

Compilers can put constant data in read parts of memory for optimization reasons, and trying to change that data will result in UB.

0
source share

Static and constant data is often stored in a different part of your program than local variables. For constant variables, these areas are often read-only to ensure constant variables. Attempting to write in read-only memory results in "undefined" behavior because the response depends on your operating system. "undefined beheavior" means that the language does not indicate how to handle this case.

If you want a more detailed explanation regarding memory, I suggest you read this . This is a UNIX-based explanation, but similar mecanism is used in all OSs.

0
source share

All Articles