我应该如何在不使用 C++ 中的构造函数的情况下将值(而不是指针)转换为子类?
How should I cast a value (not a pointer) to a subclass without using a constructor in C++?
我有以下 classes 打算用作值类型(因为它们只存储整数):
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?
}
class foo_t<T>
只是 _foo_t
的包装,为 getptr
和 getref
成员函数提供类型安全。同样,函数 make_foo()
只是 _make_foo<T>()
的包装器。由于 foo_t<T>
是 _foo_t
的子 class 并且不添加任何字段并且没有虚拟成员,因此 foo_t<T>
对象看起来应该与 _foo_t
内存中的对象,我不想在这里调用构造函数的开销。如何安全、合规地将 _make_foo()
的 return 值从 _foo_t
转换为 foo_t<T>
,而不产生任何开销?
编辑:
根据请求,这里是上面的一些示例用法:
class SomeObject { /* ... */ };
foo_t<SomeObject> obj = make_foo<SomeObject>();
new (obj.getptr()) SomeObject();
obj.getref().doSomething();
实际上,make_foo
必须采用大小参数或其他参数。
您可以概括您的 class 模板 foo_t
以能够在不依赖于 _foo_t
的情况下保存任何数据类型。您可以只使用:
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>();
}
这是一个演示其用法的示例程序:
#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;
}
程序输出:
Object1 - a:10, b: 20
Object2 - a:10, b: 20.35
Object3 - a:10.92, b: 20.35
更新
鉴于您的评论,我认为您只需要:
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());
}
How can I cast the return value of _make_foo() from _foo_t to foo_t safely, compliantly, and without creating any overhead?
你绝对不能 安全地转换 因为你不知道向下转换是有效的。但是,在这种特殊情况下,由于我们的派生类型与基类型大小相同,您可以使用:
template <typename T>
foo_t<T> make_foo()
{
return static_cast<foo_t<T>&>(_make_foo());
}
也就是说,虽然这在 CRTP 世界中是有意义的,但我不确定它在这里是否有意义。
我有以下 classes 打算用作值类型(因为它们只存储整数):
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?
}
class foo_t<T>
只是 _foo_t
的包装,为 getptr
和 getref
成员函数提供类型安全。同样,函数 make_foo()
只是 _make_foo<T>()
的包装器。由于 foo_t<T>
是 _foo_t
的子 class 并且不添加任何字段并且没有虚拟成员,因此 foo_t<T>
对象看起来应该与 _foo_t
内存中的对象,我不想在这里调用构造函数的开销。如何安全、合规地将 _make_foo()
的 return 值从 _foo_t
转换为 foo_t<T>
,而不产生任何开销?
编辑:
根据请求,这里是上面的一些示例用法:
class SomeObject { /* ... */ };
foo_t<SomeObject> obj = make_foo<SomeObject>();
new (obj.getptr()) SomeObject();
obj.getref().doSomething();
实际上,make_foo
必须采用大小参数或其他参数。
您可以概括您的 class 模板 foo_t
以能够在不依赖于 _foo_t
的情况下保存任何数据类型。您可以只使用:
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>();
}
这是一个演示其用法的示例程序:
#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;
}
程序输出:
Object1 - a:10, b: 20 Object2 - a:10, b: 20.35 Object3 - a:10.92, b: 20.35
更新
鉴于您的评论,我认为您只需要:
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());
}
How can I cast the return value of _make_foo() from _foo_t to foo_t safely, compliantly, and without creating any overhead?
你绝对不能 安全地转换 因为你不知道向下转换是有效的。但是,在这种特殊情况下,由于我们的派生类型与基类型大小相同,您可以使用:
template <typename T>
foo_t<T> make_foo()
{
return static_cast<foo_t<T>&>(_make_foo());
}
也就是说,虽然这在 CRTP 世界中是有意义的,但我不确定它在这里是否有意义。