我应该如何在不使用 C++ 中的构造函数的情况下将值(不是指针)转换为子类?

Posted

技术标签:

【中文标题】我应该如何在不使用 C++ 中的构造函数的情况下将值(不是指针)转换为子类?【英文标题】:How should I cast a value (not a pointer) to a subclass without using a constructor in C++? 【发布时间】:2015-02-10 21:13:11 【问题描述】:

我有以下类打算用作值类型(因为它们只存储一个整数):

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?

foo_t&lt;T&gt; 类只是_foo_t 的包装器,它为getptrgetref 成员函数提供类型安全。同样,函数make_foo() 只是_make_foo&lt;T&gt;() 的包装。由于foo_t&lt;T&gt;_foo_t 的子类并且不添加任何字段并且没有虚拟成员,所以foo_t&lt;T&gt; 对象应该与内存中的_foo_t 对象完全相同,我不希望这里构造函数调用的开销。如何安全、合规且不产生任何开销,将_make_foo() 的返回值从_foo_t 转换为foo_t&lt;T&gt;

编辑:

根据请求,这里是上面的一些示例用法:

class SomeObject  /* ... */ ;

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

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

实际上,make_foo 必须采用大小参数之类的。

【问题讨论】:

如果只存储整数,为什么需要这么复杂的脚手架?但是,如果您想对值类型进行类型擦除,您通常使用 Variant 或 Union 类型。 @MorphingDragon:这对问题并不重要,但如果你想知道,整数实际上是指向虚拟内存映射的指针,我想要类型安全的解引用(实际上,我正在使用 @ 987654337@ 等而不是 getrefgetptr)。 查看我的评论编辑。 @Matt 如果我正确理解了您的代码的意图,请使用组合而不是继承。让 foo_t 包含一个 _foo_t 并将任何相关的方法调用委托给它。 @Matt 微不足道的构造函数调用在任何有价值的编译器中都被优化掉了。 【参考方案1】:

如何安全、合规地将 _make_foo() 的返回值从 _foo_t 转换为 foo_t,而不会产生任何开销?

你绝对不能安全地投射,因为你不知道向下投射是有效的。但是,在这种特殊情况下,由于我们的派生类型与基类型的大小相同,因此您可以避免:

template <typename T>
foo_t<T> make_foo()

    return static_cast<foo_t<T>&>(_make_foo());

也就是说,虽然这在 CRTP 世界中是有意义的,但我不确定这是否有意义。

【讨论】:

我没想过要转换为引用,但它不起作用,因为_make_foo() 返回一个临时值,不能转换为引用:error: invalid static_cast from type ‘_foo_t’ to type ‘const foo_t&lt;SomeObject&gt;&amp;’ 如果您想知道,我最终选择了_foo_t:template &lt;typename T&gt; explicit operator foo_t&lt;T&gt;() const return *static_cast&lt;foo_t&lt;T&gt;*&gt;(this); 中的模板化转换运算符,这已经足够相似了。【参考方案2】:

您可以概括您的类模板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;

程序的输出:

对象 1 - a:10, b: 20 对象 2 - a:10,b:20.35 对象 3 - a:10.92,b:20.35

更新

鉴于您的 cmets,我认为您所需要的只是:

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());

【讨论】:

这不是我的班级正在做的。它实际上并不包含任何东西,只是一个映射到虚拟地址的整数值。它不应该是一个存储容器。 _foo_t 的原因是将大部分代码放在库中,而不是在头文件中,foo_t&lt;T&gt; 只是一个类型安全的包装器。 也许我很困惑。 _foo_t::getptr() 怎么知道Object1SomeObject 需要多少内存?它会从哪里获得内存? 这就是贴出点点滴滴代码的问题。您可以发布完整的相关代码,也可以在没有代码帮助的情况下解释您在做什么。 @Matt 这是 XY 问题。告诉我们您想要实现什么,而不是拘泥于这种次优架构。 您不能以符合标准的方式将基类对象视为派生类对象。我猜这就是您正在寻找的答案

以上是关于我应该如何在不使用 C++ 中的构造函数的情况下将值(不是指针)转换为子类?的主要内容,如果未能解决你的问题,请参考以下文章

如何在不使用向量的情况下将数组传递给函数?

如何在构造函数中访问类变量以在不使用 C++ 中的 this 指针的情况下分配它们

如何在不使用 C++/C 中的阻塞函数的情况下将值从线程返回到主函数

如何在不复制的情况下将画布 imageData 传递给 emscripten c++ 程序?

如何:在不使用 atof 的情况下将 char 字符串的值转换为 double back

如何在不使用ViewData的情况下将对象属性绑定到DropDownList中的数据源