The following code is, as far as I understand it, undefined behavior according to the c++ standard (section 7.1.5.1.4 [dcl.type.cv]/4 in particular).
#include <iostream>
struct F;
F* g;
struct F {
F() : val(5)
{
g = this;
}
int val;
};
const F f;
int main() {
g->val = 8;
std::cout << f.val << std::endl;
}
However, this prints '8' with every compiler and optimization setting I have tried.
Question : Is there an example that will exhibit unexpected results with this type of "implicit const_cast"?
I am hoping for something as spectacular as the results of
#include <iostream>
int main() {
for (int i = 0; i <=4; ++i)
std::cout << i * 1000000000 << std::endl;
}
on, eg, gcc 4.8.5 with -O2
EDIT : the relevant section from the standard
7.1.5.1.4: Except that any class member declared mutable (7.1.1) can be modified, any attempt to modify a const object during its lifetime (3.8) results in undefined behavior.
In reply to the comment suggesting a duplicate; it is not a duplicate because I am asking for an example where "unexpected" results occur.
Not as spectacular:
fh
(guards omitted):
struct F;
extern F* g;
struct F {
F() : val(5)
{
g = this;
}
int val;
};
extern const F f;
void h();
TU1:
#include "f.h"
// definitions
F* g;
const F f;
void h() {}
TU2:
#include "f.h"
#include <iostream>
int main() {
h(); // ensure that globals have been initialized
int val = f.val;
g->val = 8;
std::cout << (f.val == val) << '\n';
}
Prints 1
when compiled with g++ -O2
, and 0
when compiled with -O0
.
The main case of "undefined" behavior would be that typically, if someone sees const
, they will assume that it does not change. So, const_cast
intentionally does something that many libraries and programs would either not expect to be done or consider as explicit undefined behavior. It's important to remember that not all undefined behavior comes from the standard alone, even if that is the typical usage of the term.
That said, I was able to locate a place in the standard library where such thinking can be applied to do something I believe would more narrowly be considered undefined behavior: generating an std::map
with "duplicate keys":
#include "iostream"
#include "map"
int main( )
{
std::map< int, int > aMap;
aMap[ 10 ] = 1;
aMap[ 20 ] = 2;
*const_cast< int* >( &aMap.find( 10 )->first ) = 20;
std::cout << "Iteration:" << std::endl;
for( std::map< int,int >::iterator i = aMap.begin(); i != aMap.end(); ++i )
std::cout << i->first << " : " << i->second << std::endl;
std::cout << std::endl << "Subscript Access:" << std::endl;
std::cout << "aMap[ 10 ]" << " : " << aMap[ 10 ] << std::endl;
std::cout << "aMap[ 20 ]" << " : " << aMap[ 20 ] << std::endl;
std::cout << std::endl << "Iteration:" << std::endl;
for( std::map< int,int >::iterator i = aMap.begin(); i != aMap.end(); ++i )
std::cout << i->first << " : " << i->second << std::endl;
}
The output is:
Iteration:
20 : 1
20 : 2
Subscript Access:
aMap[ 10 ] : 0
aMap[ 20 ] : 1
Iteration:
10 : 0
20 : 1
20 : 2
Built with g++.exe (Rev5, Built by MSYS2 project) 5.3.0
.
Obviously, there is a mismatch between the access keys and the key values in the stored pairs. It also seems that the 20:2 pair is not accessible except via iteration.
My guess is that this is happening because map
is implemented as a tree. Changing the value leaves it where it initially was (where 10
would go), so it does not overwrite the other 20
key. At the same time, adding an actual 10
does not overwrite the old 10
because on checking the key value, it's not actually the same
I do not have a standard to look at right now, but I would expect this violates the definition of map
on a few levels.
It might also lead to worse behavior, but with my compiler/OS combo I was unable to get it to do anything more extreme, like crash.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.