CS253: Software Development with C++

Fall 2022

Casting

Show Lecture.Casting as a slide show.

CS253 Casting

Caution

Overview

C-style casting

In C, casting worked like this:

float f = 3.14;
unsigned int *p = &f;  // 🦡
printf("%#08x", *p);
c.c: In function 'main':
c.c:2: warning: initialization of 'unsigned int *' from incompatible pointer type 'float *'
0x4048f5c3
float f = 3.14;
unsigned int *p = (unsigned int *) &f;
printf("%#08x", *p);
0x4048f5c3

This works in C++, but don’t. Detect with g++ -Wold-style-cast.

Constructor “casting”

This isn’t really casting, but sure is similar:

int n;
n = int(3.14);
cout << "n is " << n << '\n';
n is 3

Of course, an actual programmer would just do this, even though a fussy compiler might issue a warning:

int n = 3.14;
cout << "n is " << n << '\n';
n is 3

With absolute power comes bad code

The problem with C-style casting is that it’s too powerful. It’s an “anything goes” sort of operation:

cout << (long) "Krypto the Superdog";  // 🦡
4196344

That’s the address of the string (array of const char) as a decimal number, a thing of questionable value. Hope it fits into a long!

C++ casting

C++ breaks up the vast power of the C-style cast into separate tools:

When you see one of the C++ casts, you and the compiler know what the programmer was trying to do.

const_cast

char *p = "😦";  // 🦡
cout << p;
c.cc:1: warning: ISO C++ forbids converting a string constant to ‘char*’
😦

That failed, because p is type char *, whereas "☹" is type const char[] or const char *. You can’t assign a const char * address to a char * pointer, because that would result in read/write access to a read-only location. That’s upgrading your access, which is not something to be done casually.

Downgrading your access is fine, however. You can assign a char * address to a const char * pointer, because that results in a pointer with less power. Even though I own a book, and have the legal write to destroy it, I can choose not to.

const_cast

const_cast<type>(value) removes constness. You have to specify the new type, but it must be the original type with const removed.

char *p = const_cast<char *>("😀");
cout << p;
😀

const_cast

However, const_cast can only change constness, not type:

const double *p = const_cast<const double *>("😕");  // 🦡
cout << p;
c.cc:1: error: invalid ‘const_cast’ from type ‘const char*’ to type 
   ‘const double*’

If you see const_cast, you know that it’s only removing constness, not sneakily changing types on you.

static_cast

static_cast<type>(value): usual conversions

double d = 3.15;
int i = static_cast<int>(d);
cout << i;
3
int i = static_cast<int>("123");  // 🦡
cout << i;
c.cc:1: error: invalid ‘static_cast’ from type ‘const char [4]’ to type 
   ‘int’

dynamic_cast

dynamic_cast<type>(value): Convert a pointer to a base class to a pointer to a derived class, much like static_cast, but this does type checking for polymorphic types containing virtual methods.

class Base {
  public:
    virtual void foo() { }
};
class Derived : public Base { };

Base b, *bp = &b;
Derived *dp = dynamic_cast<Derived *>(bp);
if (dp == nullptr)
    cout << "Conversion failed.\n";
else
    cout << "Conversion succeeded.\n";
Conversion failed.

reinterpret_cast

reinterpret_cast<type>(value): go nuts

double avo = 6.022e23;

long l = reinterpret_cast<long>(&avo);
cout << l << endl;

int n = *reinterpret_cast<int *>(&avo);
cout << n << endl;

int *p = reinterpret_cast<int *>(17);
cout << "Wait for it: " << flush;
cout << *p;  // 🦡
140729543524112
-195565037
Wait for it: SIGSEGV: Segmentation fault

When casting is not appropriate.

string s = "3.14159265";
cout << (double) s << '\n';  // 🦡
c.cc:2: error: invalid cast from type ‘std::string’ {aka 
   ‘std::__cxx11::basic_string<char>’} to type ‘double’

string t = "3.14159265";
cout << stod(t)*2 << '\n';
6.28319

string u = "   +042";
cout << stoi(u) << '\n'
     << stoi(u, nullptr, 16) << '\n';
42
66

string v = "CS" + to_string(253);
cout << v << '\n';
CS253