2

I'm trying to provide a uniform interface for two similar types, one dealing with doubles and the other with floats.

class float_type { float_type() { /* does floaty stuff */ } float f(); }; class double_type { double_type() { /* does doubly stuff */ } double f(); }; 

I want to write a class that allocates one or the other depending on what the program needs to do. I'm perfectly fine with the result of float_type::f() being converted to double. In fact, it happens anyway. I tried to write it like this:

class union_type { bool is_double; char mem[ sizeof(double_type) > sizeof(float_type) ? sizeof(double_type) : sizeof(float_type) ]; public: float_or_double_value_reader(bool is_double) : is_double(is_double) { if (is_double) new(mem) double_type(); else new(mem) float_type(); } ~float_or_double_value_reader() { if (is_double) delete static_cast<double_type*>(mem); else delete static_cast< float_type*>(mem); } double f() { return (is_doubled ? static_cast<double_type*>(mem)->f() : static_cast< float_type*>(mem)->f() ); } }; 

But I get invalid static_cast from type 'char [128]' to type 'double_type'.

I know I could add a member pointers to point to what new returns, but that would be redundant, since I already know where mem is located, so I want to avoid that.

If I use reinterpret_cast instead, I get free(): invalid pointer: at runtime when the union_type is destroyed.

What's the appropriate method of casting here?

4
  • 1
    Is data supposed to be mem? Anyway, when you use placement new, you don't use delete on the object, because that will try to deallocate memory. Instead, explicitly call the appropriate destructor (like reinterpret_cast<double_type*>(data)->~double_type();) Commented Jan 19, 2017 at 2:16
  • Yes, data is mem. Apologies for inconsistency. Let me fix it. Commented Jan 19, 2017 at 2:32
  • @ChristopherOicles Thanks. I was not aware of that. Could you write that as an answer? Commented Jan 19, 2017 at 2:41
  • Go ahead and accept immibis' answer Commented Jan 19, 2017 at 2:52

3 Answers 3

2

reinterpret_cast should be the appropriate method of casting.

However, you can't simply delete reinterpret_cast<double_type*>(mem) because that will not only destroy the object, but also free the memory as if it was allocated with new - which it wasn't.

You can use reinterpret_cast<double_type*>(mem)->~double_type(); to destroy the object without attempting to free the memory.

Of course the above applies to float_type as well.

Sign up to request clarification or add additional context in comments.

Comments

0

A better option would be to provide casting operator.

I would have provided a implicit double casting operator to the float class to achieve the same interface

2 Comments

That would work in some cases, but in my case, the constructors of float_type and double_type do different things, and I need to call one or the other, but not both. Dynamic allocation is of course an option, but why no do it statically?
In that case a better option would be to prefer composition over inheritance for a cleaner interface
0

You could use a template base class:

#include <iostream> template < typename T > class base_decimal { public: base_decimal(T data) : _data(data) {} virtual ~base_decimal() {} T f() { return this->_data; } base_decimal& operator=(T val) { this->_data = val; } operator T() { return this->_data; } friend std::ostream& operator<<(std::ostream& os, const base_decimal& bd) { os << bd._data; return os; } private: T _data; }; class float_type : public base_decimal<float> { public: float_type(float f) : base_decimal<float>(f) { // do float stuff } }; class double_type : public base_decimal<double> { public: double_type(double d) : base_decimal<double>(d) { // do double stuff } }; int main(int argc, char* argv[]) { float_type f = 1.2f; double_type d = 2.2; std::cout << "f = " << f << std::endl; std::cout << "d = " << d << std::endl; double rd = d; double rf = f; std::cout << "rf = " << rf << std::endl; std::cout << "rd = " << rd << std::endl; return 0; } 

1 Comment

This would definitely be applicable in a different situation, but I needed to construct one or the other (not both) for a given constructor argument. And the two types are from a library in my case, and I have no control of them.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.