precarious
precarious

Reputation: 598

Is casting non-polymorphic structs with common base and layout valid C++?

Is the following code valid C++? Otherwise, is there a valid way to simultaneously interpret memory as values of different type?

#include <cstdio>
struct Base { int payload; };
struct D1 : Base { void operator()(){ printf("D1: %d\n", payload);} };
struct D2 : Base { void operator()(){ printf("D2: %d\n", payload);} };

int main()
{
   D1 d1;
   D2& d2 = static_cast<D2&>(static_cast<Base&>(d1));
   d1();
   d2();
   d2.payload = 3;
   d1();
   d2();
}

In response to @NickoPo: My use case is basically what follows. Imagine that IntBase is not necessarily cheap to copy, that there are many complex algorithms, some of which profit from numbers being prime vs. odd, and others don't:

#include <cassert>
#include <cstdio>

bool is_odd(int value) { return 0 != value % 2; }
bool is_small_prime(int value) { return 2 == value || 3 == value || 5 == value || 7 == value; }

class IntBase
{
   public:
      explicit IntBase(int value) : m_value(value) {}
      int value() const { return m_value; }
   protected:
      int m_value;
};

class OddInt : public IntBase
{
   public:
      explicit OddInt(int value) : IntBase(value) { assert(is_odd(m_value)); }
};

class SmallPrimeInt : public IntBase
{
   public:
      explicit SmallPrimeInt(int value) : IntBase(value) { assert(is_small_prime(m_value)); }
};

bool is_constrainable_to_odd_int(IntBase const& x)
{
   return is_odd(x.value());
}

OddInt const& constrain_to_odd_int(IntBase const& x)
{
   assert(is_odd(x.value()));
   return static_cast<OddInt const&>(x);
}

bool is_constrainable_to_small_prime_int(IntBase const& x)
{
   return is_small_prime(x.value());
}

SmallPrimeInt const& constrain_to_small_prime_int(IntBase const& x)
{
   assert(is_small_prime(x.value()));
   return static_cast<SmallPrimeInt const&>(x);
}

void algorithm(IntBase const&)
{
   printf("algoritm(IntBase const&)\n");
}

void algorithm(OddInt const&)
{
   printf("algoritm(OddInt const&)\n");
}

void algorithm(SmallPrimeInt const&)
{
   printf("algoritm(SmallPrimeInt const&)\n");
}

void test(IntBase const& x)
{
   if (is_constrainable_to_small_prime_int(x))
   {
      algorithm(constrain_to_small_prime_int(x));
   }
   else if (is_constrainable_to_odd_int(x))
   {
      algorithm(constrain_to_odd_int(x));
   }
   else
   {
      algorithm(x);
   }
}

void test(OddInt const& x)
{
   if (is_constrainable_to_small_prime_int(x))
   {
      algorithm(constrain_to_small_prime_int(x));
   }
   else
   {
      algorithm(constrain_to_odd_int(x));
   }
}

int main()
{
   IntBase x(0);
   OddInt y(1);
   OddInt z(7);

   test(x); // algoritm(IntBase const&)
   test(y); // algoritm(OddInt const&)
   test(z); // algoritm(SmallPrimeInt const&)
}

Related:

Upvotes: 0

Views: 146

Answers (2)

n. m. could be an AI
n. m. could be an AI

Reputation: 120031

If your objects are not cheap to copy, you are probably passing pointers or references everywhere. You can wrap pointers to your common base in different class types and pass them by value. That is, instead of this (pseudocode)

class B
class D1 : B { ... }
class D2 : B { ... }

D1* d1; D2* d2;

you have

class Bimpl 

class B { Bimpl* bimpl; }
class D1 : B { ... }
class D2 : B { ... }

D1 d1; D2 d2;

Here you never do any built-in cast. If you want to convert D1 to D2, you write your own conversion function.

Upvotes: 0

CinchBlue
CinchBlue

Reputation: 6190

If you're going to cast while using similar interfaces while using type as a guarantee, I'd recommend that you just wrap the inner data with your new object type and then provide access to the inner data in order to transfer it from one type to another. There's no point in doing static casting or reinterpret casting if you're not going to do it safely.

Here's an example:

http://coliru.stacked-crooked.com/a/40d5efeff22fcdcd

#include <iostream>

//Base data structure to encapsulate only data.
struct data {
    data(int i) : i(i) {}

    int i;   
};

//Wrapper around our data structure, with interfaces to access
//the values and the data; implement your own constructor to
//gate the value
class PrimeInt {
public:
    PrimeInt(const int i)
    : d(i) {}

    PrimeInt(const data& other)
    : d(other) {}

    PrimeInt(data&& other)
    : d(std::move(other)) {}

    PrimeInt& operator=(const PrimeInt&) = default;
    PrimeInt& operator=(PrimeInt&&) = default;

    int get() {return d.i;};

    operator data() {return d;};

private:
    data d;
};

//Wrapper around our data structure, with interfaces to access
//the values and the data; implement your own constructor to
//gate the value
class OddInt {
public:
    OddInt(const int i)
    : d(i) {}

    OddInt(const data& other)
    : d(other) {}

    OddInt(data&& other)
    : d(std::move(other)) {}

    OddInt& operator=(const OddInt&) = default;
    OddInt& operator=(OddInt&&) = default;

    int get() {return d.i;};

    operator data() {return d;};

private:
    data d;
};

//Notice that we can now implicitly cast from one type to another.
int main() {
    PrimeInt pi(10);

    std::cout << pi.get() << std::endl;

    OddInt oi(pi);

    std::cout << oi.get() << std::endl;

    return 0;
}

Upvotes: 1

Related Questions