I am trying to understand some details of static_cast.
Please have a look at the following code,
struct A
{
int data = 0;
};
void foo(const A* a)
{
(*static_cast<A**>(static_cast<void*>(&a)))->data = 1;
}
void bar(const A* a)
{
const_cast<A*>(a)->data = 1;
}
int main()
{
A a;
foo(&a);
return a.data;
}
Is the function foo valid C++ code?
Is there any valid usage that gives a different result with foo vs. bar?
Both functions are valid C++ and have well-defined behavior (modifying the data member of the A object created in main) in C++11 and later.
You are allowed to obtain a pointer to non-const from a pointer to const object type either directly with const_cast or indirectly with static_cast going through void* as you are doing in foo. That in itself is not a problem:
The cast to void* is possible because &a is a pointer to const A* which is not (top-level) cv-qualified. The cast from void* to A** is possible because void* can be cast to any object pointer type. Dereferencing the result is accessing the const A* object through a pointer to A*, but that is ok because the types are similar. (The last part seems to have been an aliasing rule violation prior to C++11, making this undefined behavior.)
However, modifying a const qualified object through a pointer to non-const obtained in such a way causes undefined behavior.
Since the object A a; that you are passing to the function is not const qualified, there is no problem.
But I think it is obvious why using such functions is dangerous. Changing the declaration A a; to const A a; will still compile because there is no type mismatch, but will have undefined behavior.
Both functions do exactly the same thing in all situations, as far as I can tell.
Related
int* b = new int(40);
int c = *(int *)b;
The above cast is working fine.
But similar casting is not working for function pointers
void abc(int a){
cout<<a<<endl;
}
std::function<void(int)> callback = *(std::function<void(int)>*)abc; // this cast is not working
What is wrong in the above piece of code?
You cast b to the same type that it already has. This results in a static cast, which doesn't change the value. The type of b is int* and you cast to int*.
You cast abc to an entirely different type. And since the target type is unrelated, this resulst in a reinterpret cast, and accessing the pointed object through the reinterpreted pointer (which is a problem since it points to a function and not an object at all) results in undefined behaviour. The type of abc is void(int) which is a function type and it implicitly decays to void(*)(int) which is a pointer to function type. You cast it to std::function<void(int)>* which is a pointer to object type, where the object type is of the class type that was instantiated from the class template std::function.
What is wrong in the above piece of code?
Using C style cast is wrong. Don't do it.
Reinterpret casting pointer to an unrelated type is wrong. std::function<...>* is not a pointer to function. std::function is not a function. It's a class template for a function wrapper.
std::function<void(int)> is not a function. It is a class with an operator(). It has a constructor that you need to invoke to create an instance of that class. For example like this:
std::function<void(int)> callback = abc;
On the other hand, here:
int* b = new int(40);
int c = *(int *)b;
new int(40) does create an int object and b is a pointer to that object. The cast (int*)b doesn't do anything, because b is already a int* that you can dereference to assign the value of the int to c.
I tried to keep it simple, for a more accurate explanation of what is actually happening in your code I refer you to this answer: https://stackoverflow.com/a/70166958/4117728.
Will the following work as expected?:
struct A {};
struct B: public A {
int x;
};
void f( B* o ) {
std::cout << o->x << std::endl;
}
int main () {
B b;
b.x = 5;
reinterpret_cast<void(*)(A*)>(f)( &b );
}
Its undefined behaviour to use such pointer after cast:
Any pointer to function can be converted to a pointer to a different function type. Calling the function through a pointer to a different function type is undefined, but converting such pointer back to pointer to the original function type yields the pointer to the original function.
From http://en.cppreference.com/w/cpp/language
So the answer to your question is actually positive - you are allowed to cast but nothing more.
You might ask "what is the point of only casting?" - this is usefull when you want to store various functions in single collection.
See 5.2.10/6 [expr.reinterpret.cast]:
A function pointer can be explicitly converted to a function pointer of a different type. The effect of calling a function through a pointer to a function type that is not the same as the type used in the definition of the function is undefined.
That said, note as an example that C++ allows you to dereference a null pointer, so maybe allowed is not the right term.
The following command compiles too:
reinterpret_cast<void(*)(A*, int)>(f)( &b, 42 );
It is allowed, as well as the one in the question, no matter if it works as expected or not (it mostly depends on your expectations, as noted by #luk32 in the comments).
The answer to your question would be yes, the cast is allowed, but the invokation of the function through the new pointer leads to an undefined behavior.
Here's a code snippet that hopefully conveys what I'm trying to do:
void updatePointer(const int*& i)
{
i++;
}
int main() {
int array[5];
int* arrayPtr = array;
updatePointer(arrayPtr );
return 0;
}
This gives compiler error:
prog.cpp: In function ‘int main()’:
prog.cpp:16: error: invalid initialization of reference of type ‘const int*&’ from
expression of type ‘int*’
prog.cpp:5: error: in passing argument 1 of ‘void updatePointer(const int*&)’
Supposing that you could do it, you could write the following:
const int c = 0;
void updatePointer(const int* &i) {
i = &c;
}
int main() {
int *ptr;
updatePointer(ptr);
*ptr = 1; // attempt to modify the const object c, undefined behavior
}
The purpose of const is to ensure that user code cannot attempt to modify a const object unless it contains a const-cast (or equivalent). So the compiler has to refuse this code. Forbidding a const int*& from binding to an int* is the only place in the code above that's reasonable for the compiler to refuse: every other line is fine.
It's the same reason you can't implicitly convert int** to const int **.
Aside from the motivation in terms of const-safety, you can think if it in terms of int* being a different type from const int*, that just so happens to be convertible to it. Likewise, you can convert int to double, but a double& can't bind to an int lvalue. That's not the full reason, because actually int* and const int* have the same size and representation, whereas int and double don't. So there could be a special-case to allow it if not for the fact that it would break the const system.
The reason that C++ has both const and non-const overloads for strchr is related to this issue: your function updatePointer modifies its input rather than returning the updated value, but the principle is similar. The C-style single strchr allows you to "launder" a pointer-to-const into a pointer-to-non-const without a cast, and it's a hole in the const system. C++ (a) has overloading and (b) has a stricter type system than C, so it closes that hole.
If you want your real function updatePointer to work like strchr -- examine the data pointed to and compute a new value for the pointer, then you're in the same situation that strchr is. That's regardless of what it does with the new value (return it in the case of strchr, write it back in the case of updatePointer), because the issue is that you want the new pointer to have the same const-qualification as the input. You need to provide either const- and non-const overloads or a function template.
If you only need your real function updatePointer to move a pointer by a certain distance, regardless of the data pointed to, you could use std::advance instead.
What you wrote is a function taking a reference to a pointer to a const int. What you're asking for would be
updatePointer(int* const & i);
However this doesn't make much sense. Passing a reference to a pointer seems to imply that you intend to modify the pointer, but you cannot do it because it is declared const. As it is you'd obtain the same effect by just passing your pointer as in
updatePointer(int* i);
Found this
Copied here in case the link breaks in future:
The reasoning is a little awkward to comes to grips with. The main question is:
Since a "const int&" can be bound to an "int", why can't a "const int*&" be bound to a "int*"?
Basically, once you add a level of indirection (a pointer) then the rules change. With just a single level of indirection (as in a single *), the rule can be stated as:
A reference to a pointer to a cv-qualified type can be bound to anything of that same type whose cv-qualifications are less than or equal to that of the pointer (which is a reference).
(Read that a few times.)
So the reason a "const int*&" can't be bound to a "int*" is because "const int*" and "int*" are two different types (underlined part of the rule is broken).
The following code compile well both with GCC (4.2-4.6) and with Clang (2.1), but when I run the executable it gives me "Bus error: 10". I don't understand the reason.
#include <iostream>
struct A
{
static int const v;
A() { ++*const_cast<int *>(&A::v); }
};
int const A::v = 0;
int main(int argc, char * argv[])
{
A a, b, c;
std::cout << a.v << std::endl;
return 0;
}
I think the relevant quote is:
§ 7.1.6.1 (4) from N3242:
Except that any class member declared mutable can be modified, any
attempt to modify a const object during its lifetime results in
undefined behavior.
The examples illustrate the point using const_cast. As James pointed out: the quote can be found in §7.1.5 in the C++03 standard.
A little elaboration: That language rule allows the compiler to use read-only memory (if it is available on the target architecture) when something is declared const. Without this rule const-ness could always be casted away without fearing any consequences and using it would only be a matter of developer discipline. The way it is you can at least tell people that they are invoking UB, which usually is a good deterrent. The const_cast itself is of minor relevance as it does not matter how you trick the compiler in letting you manipulate a const object.
5.2.11.7:
Depending on the type of the object, a write operation through the
pointer, lvalue or pointer to data member resulting from a const_cast
that casts away a const-qualifier) may produce undefined behavior
(7.1.5.1)
In your case, you are trying to modify data that is in read-only segment.
Because you're not allowed to modify variables declared as const.
Just because you've cast away const, doesn't mean that you will succeed in writing to that memory.
All that const_cast<T> does is remove the const-ness of the variable from the compiler's perspective. That lets the compiler go ahead and emit code to write to the variable. But at runtime, if the compiler/linker happened to put the variable in read-only memory, then the hardware will stop you writing there no matter how you cast it.
I don't have a solution for the actual problem. I just can say, don't use const_cast unless the intention is to call a const member function from a non-const member function and "const_cast" the const result (to make it a mutable result for the non-const member function).
But I have a proposal for improving your design:
class A
{
private:
static int v;
public:
A() { ++v; }
static int get_v() { return v; }
};
int A::v = 0;
int main(int argc, char * argv[])
{
A a, b, c;
std::cout << a.get_v() << std::endl;
return 0;
}
Basically, if a variable is declared const, the compiler is allowed to emit the results to read only memory. Taking a pointer/reference to a const object and then using const_cast to remove the const can result in undefined behavior.
In general, it is only safe to use const_cast if the object being refered to is non-const (even if the pointer/reference you have is const).
The problem is this line :
static int const v;
Because you declared it const, the const_cast is causing an undefined behaviour - in your case you are lucky with getting bus error (it is a segmentation fault on my system).
Declare it non-const, and you can call const_cast on it without problems.
From the book Exceptional C++ Solution to ch. 44 I've learned that they are situations when none of the new cast styles would work properly. I always thought that they (those 4 new casts) cover every possible situation and there is no need for "old" style cast anymore, but it appears to be not true. So my question is:
Are those new casts cover all possible situations so there is no need to ever use c-style cast or:
There are situation in which only the old cast works properly?
Thanks.
That's appropriate fragment from this book:
"
class A { public: virtual ~A(); /*...*/ };
A::~A() { }
class B : private virtual A { /*...*/ };
class C : public A { /*...*/ };
class D : public B, public C { /*...*/ };
A a1; B b1; C c1; D d1;
const A a2;
const A& ra1 = a1;
const A& ra2 = a2;
char c;
void f()
{
A* pa; B* pb; C* pc;
pa = (A*)&ra1;
pa = (A*)&a2;<<----------This is the cast I'm interested in
//This cannot be expressed as a new-style cast. The closest candidate is const_cast,
//but because a2 is a const object, the results of using the pointer are undefined.
//Not my words those are words of Herb Sutter. (whose style of writing irritates me to bits)
pb = (B*)&c1;
pc = (C*)&d1;
}
"
EDITED
Chapter 44 from Exceptional C++:
"Item 44. Casts
Difficulty: 6
How well do you know C++'s casts? Using them well can greatly improve the reliability of your code.
The new-style casts in standard C++ offer more power and safety than the old-style (C-style) casts. How well do you know them? The rest of this problem uses the following classes and global variables:
class A { public: virtual ~A(); /*...*/ };
A::~A() { }
class B : private virtual A { /*...*/ };
class C : public A { /*...*/ };
class D : public B, public C { /*...*/ };
A a1; B b1; C c1; D d1;
const A a2;
const A& ra1 = a1;
const A& ra2 = a2;
char c;
This Item presents four questions.
Which of the following new-style casts are not equivalent to a C-style cast?
const_cast
dynamic_cast
reinterpret_cast
static_cast
For each of the following C-style casts, write the equivalent new-style cast. Which are incorrect if not written as a new-style cast?
void f()
{
A* pa; B* pb; C* pc;
pa = (A*)&ra1;
pa = (A*)&a2;
pb = (B*)&c1;
pc = (C*)&d1;
}
Critique each of the following C++ casts for style and correctness.
void g()
{
unsigned char* puc = static_cast<unsigned char*>(&c);
signed char* psc = static_cast<signed char*>(&c);
void* pv = static_cast<void*>(&b1);
B* pb1 = static_cast<B*>(pv);
B* pb2 = static_cast<B*>(&b1);
A* pa1 = const_cast<A*>(&ra1);
A* pa2 = const_cast<A*>(&ra2);
B* pb3 = dynamic_cast<B*>(&c1);
A* pa3 = dynamic_cast<A*>(&b1);
B* pb4 = static_cast<B*>(&d1);
D* pd = static_cast<D*>(pb4);
pa1 = dynamic_cast<A*>(pb2);
pa1 = dynamic_cast<A*>(pb4);
C* pc1 = dynamic_cast<C*>(pb4);
C& rc1 = dynamic_cast<C&>(*pb2);
}
Why is it typically unuseful to const_cast from non-const to const? Demonstrate a valid example in which it can be useful to const_cast from non-const to const."
Solution to chapter 44
Solution
Let's answer the questions one by one.
Which of the following new-style casts are not equivalent to a C-style cast?
Only dynamic_cast is not equivalent to a C-style cast. All other new-style casts have old-style equivalents.
Guideline
Prefer new-style casts.
For each of the following C-style casts, write the equivalent new-style cast. Which are incorrect if not written as a new-style cast?
void f()
{
A* pa; B* pb; C* pc;
pa = (A*)&ra1;
Use const_cast instead:
pa = const_cast<A*>(&ra1);
pa = (A*)&a2;
This cannot be expressed as a new-style cast. The closest candidate is const_cast, but because a2 is a const object, the results of using the pointer are undefined.
pb = (B*)&c1;
Use reinterpret_cast instead:
pb = reinterpret_cast<B*>(&c1);
pc = (C*)&d1;
The above cast is wrong in C. In C++, no cast is required:
pc = &d1;
}
Critique each of the following C++ casts for style and correctness.
First, a general note: All of the following dynamic_casts would be errors if the classes involved did not have virtual functions. Fortunately, A does provide a virtual function, making all the dynamic_casts legal.
void g()
{
unsigned char* puc = static_cast<unsigned char*>(&c);
signed char* psc = static_cast<signed char*>(&c);
Error: We must use reinterpret_cast for both cases. This might surprise you at first, but the reason is that char, signed char, and unsigned char are three distinct types. Even though there are implicit conversions between them, they are unrelated, so pointers to them are unrelated.
void* pv = static_cast<void*> (&b1);
B* pb1 = static_cast<B*>(pv);
These are both fine, but the first is unnecessary, because there is already an implicit conversion from a data pointer to a void*.
B* pb2 = static_cast<B*> (&b1);
This is fine, but unnecessary, since the argument is already a B*.
A* pa1 = const_cast<A*>(&ra1);
This is legal, but casting away const (or volatile) is usually indicative of poor style. Most of the cases in which you legitimately would want to remove the const-ness of a pointer or reference are related to class members and covered by the mutable keyword. See Item 43 for further discussion of const-correctness.
Guideline
Avoid casting away const. Use mutable instead.
A* pa2 = const_cast<A*>(&ra2);
Error: This will produce undefined behavior if the pointer is used to write on the object, because a2 really is a const object. To see why this is a legitimate problem, consider that a compiler is allowed to see that a2 is created as a const object and use that information to store it in read-only memory as an optimization. Casting away const on such an object is obviously dangerous.
Guideline
Avoid casting away const.
B* pb3 = dynamic_cast<B*>(&c1);
Potential error (if you try to use pb3): Because c1 IS-NOT-A B (because C is not publicly derived from B—in fact, it is not derived from B at all), this will set pb3 to null. The only legal cast would be a reinterpret_cast, and using that is almost always evil.
A* pa3 = dynamic_cast<A*>(&b1);
Probable error: Because b1 IS-NOT-AN A (because B is not publicly derived from A; its derivation is private), this is illegal unless g() is a friend of B.
B* pb4 = static_cast<B*>(&d1);
This is fine, but unnecessary because derived-to-public-base pointer conversions can be done implicitly.
D* pd = static_cast<D*>(pb4);
This is fine, which may surprise you if you expected this to require a dynamic_cast. The reason is that downcasts can be static when the target is known, but beware: You are telling the compiler that you know for a fact that what is being pointed to really is of that type. If you are wrong, then the cast cannot inform you of the problem (as could dynamic_cast, which would return a null pointer if the cast failed) and, at best, you will get spurious run-time errors and/or program crashes.
Guideline
Avoid downcasts.
pa1 = dynamic_cast<A*>(pb2);
pa1 = dynamic_cast<A*>(pb4);
These two look very similar. Both attempt to use dynamic_cast to convert a B* into an A*. However, the first is an error, while the second is not.
Here's the reason: As noted above, you cannot use dynamic_cast to cast a pointer to what really is a B object (and here pb2 points to the object b1) into an A object, because B inherits privately, not publicly, from A. However, the second cast succeeds because pb4 points to the object d1, and D does have A as an indirect public base class (through C), and dynamic_cast is able to cast across the inheritance hierarchy using the path B* D* C* A*.
C* pc1 = dynamic_cast<C*>(pb4);
This, too, is fine for the same reason as the last: dynamic_cast can navigate the inheritance hierarchy and perform cross-casts, so this is legal and will succeed.
C& rc1 = dynamic_cast<C&>(*pb2);
}
Finally, an "exceptional" error: Because *pb2 isn't really a C, dynamic_cast will throw a bad_cast exception to signal failure. Why? Well, dynamic_cast can and does return null if a pointer cast fails, but since there's no such thing as a null reference, it can't return a null reference if a reference cast fails. There's no way to signal such a failure to the client code besides throwing an exception, so that's what the standard bad_cast exception class is for.
Why is it normally unuseful to const_cast from non-const to const?
The first three questions included no examples of using const_cast to add const, for example, to convert a pointer to non-const to a pointer to const. After all, explicitly adding const is usually redundant—for example, it's already legal to assign a pointer to non-const to a pointer to const. Normally, we only need const_cast to do the reverse.
And the last part of the question: Demonstrate a valid example where it can be useful to const_cast from non-const to const.
There is at least one case in which you could usefully const_cast from non-const to const—to call a specific overloaded function or a specific version of a template. For example:
void f( T& );
void f( const T& );
template<class T> void g( T& t )
{
f( t ); // calls f(T&)
f( const_cast<const T&>(t) ); // calls f(const T&)
}
Of course, in the case of choosing a specific version of a template, it's usually just easier to name it explicitly instead of forcing the right deduction. For example, to call the right version of a templated function h(), writing "h( t )" is preferable to writing "h( const_cast(t) )".
In that situation, const_cast will have exactly the same effect as a C cast. Both will give a non-const pointer to a constant object, and in both cases trying to modify the object will give undefined behaviour.
Any conversion can be made using some combination of C++ casts, but there are some cases where a C cast can make a conversion that no single C++ cast can. For example, reinterpret_cast can't remove const or volatile qualifications, and const_cast can't convert between two unrelated pointer types; but a C cast can do both at once:
class A;
class B;
A const* a = 0;
B* b;
b = reinterpret_cast<B*>(a); // fail: can't remove const
b = const_cast<B*>(a); // fail: can't convert between pointer types
b = reinterpret_cast<B*>(const_cast<A*>(a)); // OK
b = (B*)a; // OK
I would still prefer to see the the two casts in this case, at the cost of extra typing; it makes it clear that something freaky is going down, and uses a syntax that can be searched for. In my opinion, C casts should never be used for anything.
In C++, the old-style casts are defined in terms of the new-style casts.
5.4:
4 Any type conversion not mentioned
below and not explicitly defined by
the user (12.3) is ill-formed.
5 The conversions performed by
— a const_cast (5.2.11),
— a static_cast (5.2.9),
— a static_cast followed by a
const_cast, — a reinterpret_cast
(5.2.10), or — a reinterpret_cast
followed by a const_cast, can be
performed using the cast notation of
explicit type conversion.
The example you provided is covered quite cleanly by the first bullet. Your comment at the end of the example is only half right. You can read the result, but you can not write to it. This is the same whether you use const_cast or not. The underlying object does not lose its const-ness just because you cast it away.
A few clauses later, a few situations in which a C-style cast behaves differently from a regular static_cast are listed. But they have to do with casting along inheritances in which the base class is inaccessible. The virtual in your example suggests that maybe there was some inheritance in the book's actual code; perhaps that is what he was trying to get at, and you misunderstood?
For completeness:
7 In addition to those conversions, the
following static_cast and
reinterpret_cast operations
(optionally followed by a const_cast
operation) may be performed using the
cast notation of explicit type
conversion, even if the base class
type is not accessible:
— a pointer to
an object of derived class type or an
lvalue of derived class type may be
explicitly converted to a pointer or
reference to an unambiguous base class
type, respectively;
— a pointer to
member of derived class type may be
explicitly converted to a pointer to
member of an unambiguous non-virtual
base class type;
— a pointer to an
object of non-virtual base class type,
an lvalue of non-virtual base class
type, or a pointer to member of non-virtual base class type may be explicitly converted to a pointer, a reference, or a
pointer to member of a derived class type, respectively.
As an example of what that last clause is talking about, here is something only possible with a C-style cast.
class Base { };
class Derived : Base { };
Derived d;
Base* pb;
pb = static_cast<Base*>(&d); //inaccessible base
pb = (Base*)(&d); //just fine
However, I am finding it hard to imagine a situation where this would not be a bad idea. For practical purposes, just assume C-style casts don't exist.
The closest candidate is const_cast, but because a2 is a const object, the results of using the pointer are undefined.
Just to be clear, the C-style cast (A*)&a2 also yields undefined behavior. So const_cast is not "the closest candidate", it is the equivalent.
All that seems to prove is that everything has an edge case. I've never come across that situation in the real world.
By the way, did you have a question?