简体   繁体   中英

How should I cast a value (not a pointer) to a subclass without using a constructor in C++?

I have the following classes intended to be used as value types (since they only store an integer):

class _foo_t
{
    friend _foo_t _make_foo();
private:
    int foo;
    _foo_t(int foo) : foo(foo) {}
protected:
    void *getptr() const; // defined in .cpp
};

template <typename T>
class foo_t : public _foo_t
{
public:
    T *getptr() const { return (T*)_foo_t::getptr(); }
    T &getref() const { return *(T*)_foo_t::getptr(); }
};

_foo_t _make_foo(); // defined in .cpp

template <typename T>
foo_t<T> make_foo()
{
    return _make_foo(); // What kind of cast do I need here?
}

The class foo_t<T> is just a wrapper around _foo_t that provides type safety for the getptr and getref member functions. Likewise, the function make_foo() is just a wrapper around _make_foo<T>() . Since foo_t<T> is a subclass of _foo_t and does not add any fields and there are no virtual members, a foo_t<T> object should look exactly the same as a _foo_t object in memory, and I do not not want the overhead of a constructor call here. How can I cast the return value of _make_foo() from _foo_t to foo_t<T> safely, compliantly, and without creating any overhead?

EDIT:

Per request, here is some sample usage of the above:

class SomeObject { /* ... */ };

foo_t<SomeObject> obj = make_foo<SomeObject>();

new (obj.getptr()) SomeObject();
obj.getref().doSomething();

In reality, make_foo would have to take a size parameter or something.

How can I cast the return value of _make_foo() from _foo_t to foo_t safely, compliantly, and without creating any overhead?

You definitely can't cast safely since you don't know that the downcast is valid. However, in this particular case, since our derived type is the same size as the base type, you can get away with:

template <typename T>
foo_t<T> make_foo()
{
    return static_cast<foo_t<T>&>(_make_foo());
}

That said, while this is something that makes sense to do in a CRTP world, I'm not sure it makes sense here.

You can generalize your class template foo_t to be able to hold any data type without depending on _foo_t . You can just use:

template <typename T> class foo_t 
{
   public:
      char data[sizeof(T)];
      T *getptr() const { return (T*)data; }
      T &getref() const { return *(T*)data; }
};

template <typename T>
foo_t<T> make_foo()
{
   return foo_t<T>();
}

Here's an example program that demonstrates its usage:

#include <iostream>
#include <new>

template <typename T> class foo_t 
{
   public:
      char data[sizeof(T)];
      T *getptr() const { return (T*)data; }
      T &getref() const { return *(T*)data; }
};

template <typename T>
foo_t<T> make_foo()
{
   return foo_t<T>();
}

struct Object1
{
   int a;
   int b;
};

struct Object2
{
   int a;
   double b;
};

struct Object3
{
   double a;
   double b;
};

int main()
{
   foo_t<Object1> obj1 = make_foo<Object1>();
   new (obj1.getptr()) Object1();
   obj1.getref().a = 10;
   obj1.getref().b = 20;
   std::cout << "Object1 - a:" << obj1.getref().a << ", b: " << obj1.getref().b << std::endl;

   foo_t<Object2> obj2 = make_foo<Object2>();
   new (obj2.getptr()) Object2();
   obj2.getref().a = 10;
   obj2.getref().b = 20.35;
   std::cout << "Object2 - a:" << obj2.getref().a << ", b: " << obj2.getref().b << std::endl;

   foo_t<Object3> obj3 = make_foo<Object3>();
   new (obj3.getptr()) Object3();
   obj3.getref().a = 10.92;
   obj3.getref().b = 20.35;
   std::cout << "Object3 - a:" << obj3.getref().a << ", b: " << obj3.getref().b << std::endl;

   return 0;
}

Output of the program:

Object1 - a:10, b: 20
Object2 - a:10, b: 20.35
Object3 - a:10.92, b: 20.35

Update

Given your comments, I think all you need is:

template <typename T>
class foo_t : public _foo_t
{
   public:

      // Make sure you can use all of the base class
      // constructors in this class.
      using _foo_t::_foo_t;

      T *getptr() const { return (T*)_foo_t::getptr(); }
      T &getref() const { return *(T*)_foo_t::getptr(); }
};

template <typename T>
foo_t<T> make_foo()
{
   // Construct a foo_t<T> using a _foo_t and return it.
   return foo_t<T>(_make_foo());
}

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM