模板类对象的数组

如何解决模板类对象的数组

问题

我想要一个指向模板类实例的指针数组。如果C ++允许在基类中使用模板化的派生类进行模板化的虚拟方法,则将解决我的问题。

因此,如何实现模板化虚拟方法?

下面我有一个可行的解决方案,但是我对自己的实现方式感兴趣。

约束

template参数是无限可变的,例如,我无法枚举此模板类的每个特殊化。模板类T可以是任何POD,POD数组或POD的结构。

T的完整集合在编译时是已知的。基本上,我有一个文件,该文件定义了用于实例化对象的所有不同T,并使用Xmacros(https://en.wikipedia.org/wiki/X_Macro)创建对象数组。

我知道这不是一个好主意。让我们暂时忽略一下。最终变得更加好奇。

可能的解决方案

这些是我研究过的东西。

创建基类和派生类

class Base {
  virtual void SomeMethod() = 0;
}

template <class T>
class Derived : Base {
  void SomeMethod() {...}
}

问题是我无法声明Base中要重载的所有虚拟方法,因为无法对虚拟方法进行模板化。否则,这将是一个完美的解决方案。

std :: any / std :: variant

我正在使用C ++ 17,因此可以使用std::any定义虚拟基本方法。但是它不能保存数组,因此不能在这里使用。

CRTP

看来这无助于我创建这些不同对象的数组。我需要做类似的事情

template <typename D,typename T>
class Base
{
    ...
};

template <typename T>
class Derived : public Base<Derived,T>
{
    ...
};

所以我仍然最终尝试创建Derived<T>对象的数组。

访客模式

再次看来,我需要枚举Visitable类需要服务的每种可能的类型,尽管并非不可能(再次,我有一个文件定义了所有不同的T似乎更像Xmacros,这只会使问题变得更加复杂。

我的解决方案

这是我想出的。它将在https://www.onlinegdb.com/online_c++_compiler

中运行
#include <iostream>
#include <array>
#include <typeinfo>

// Base class which declares "overloaded" methods without implementation
class Base {
 public:
  template <class T>
  void Set(T inval);
  template <class T>
  void Get(T* retval);
  virtual void Print() = 0;
};

// Template class which implements the overloaded methods
template <class T>
class Derived : public Base {
 public:
  void Set(T inval) {
    storage = inval;
  }
  void Get(T* retval) {
    *retval = storage;
  }
  void Print() {
    std::cout << "This variable is type " << typeid(T).name() <<
      ",value: " << storage << std::endl;
  }
 private:
  T storage = {};
};

// Manually pointing base overloads to template methods
template <class T> void Base::Set(T inval) {
  static_cast<Derived<T>*>(this)->Set(inval);
}
template <class T> void Base::Get(T* retval) {
  std::cout << "CALLED THROUGH BASE!" << std::endl;
  static_cast<Derived<T>*>(this)->Get(retval);
}

int main()
{
  // Two new objects
  Derived<int>* ptr_int = new Derived<int>();
  Derived<double>* ptr_dbl = new Derived<double>();
  
  // Base pointer array
  std::array<Base*,2> ptr_arr;
  ptr_arr[0] = ptr_int;
  ptr_arr[1] = ptr_dbl;

  // Load values into objects through calls to Base methods
  ptr_arr[0]->Set(3);
  ptr_arr[1]->Set(3.14);

  // Call true virtual Print() method
  for (auto& ptr : ptr_arr) ptr->Print();

  // Read out the values
  int var_int;
  double var_dbl;
  std::cout << "First calling Get() method through true pointer." << std::endl;
  ptr_int->Get(&var_int);
  ptr_dbl->Get(&var_dbl);
  std::cout << "Direct values: " << var_int << "," << var_dbl << std::endl;
  std::cout << "Now calling Get() method through base pointer." << std::endl;
  ptr_arr[0]->Get(&var_int);
  ptr_arr[1]->Get(&var_dbl);
  std::cout << "Base values: " << var_int << "," << var_dbl << std::endl;

  return 0;
}

运行此命令时,它表明在Base上调用方法正确指向Derived实现。

This variable is type i,value: 3                                                                                                    
This variable is type d,value: 3.14                                                                                                 
First calling Get() method through true pointer.                                                                                     
Direct values: 3,3.14                                                                                                               
Now calling Get() method through base pointer.                                                                                       
CALLED THROUGH BASE!                                                                                                                 
CALLED THROUGH BASE!                                                                                                                 
Base values: 3,3.14  

基本上,我是在手动创建虚拟方法指针。但是,由于我明确地这样做,因此允许我使用Base中的模板方法,该模板方法指向Derived中的方法。更容易出错,例如,对于每个模板方法,我需要两次键入方法名称,即,我可能会弄乱:

template <class T> void Base::BLAH_SOMETHING(T inval) {
  static_cast<Derived<T>*>(this)->WHOOPS_WRONG_CALL(inval);
}

因此,这毕竟是一个可怕的主意吗?对我来说,这似乎可以达到我避免模板化虚拟方法局限性的目标。这真的有什么问题吗?我知道,可能有一些方法可以使所有不必要的代码结构化,我只是专注于这种特定的结构。

解决方法

它更容易出错,例如,对于每个模板方法,我需要两次键入方法名称

哦,这是您最不用担心的。想象一下,如果您将类型转换为错误的类型。

至少可以避免头痛,并使用dynamic_cast

class Base {
  public:
    virtual ~Base() = default;

    template <class T>
    void Set(T inval) {
        dynamic_cast<Derived<T>&>(*this).Set(inval);
    }

    template <class T>
    T Get() {
        return dynamic_cast<Derived<T>&>(*this).Get();
    }
};


template <class T>
class Derived : public Base {
  public:
    void Set(T inval) {
      storage = inval;
    }

    T Get() {
      return storage;
    }

  private:
    T storage{};
};

除此之外,我同意这些评论,这可能不是解决您问题的正确方法。

,

处理包含未知类型的子类的常规方法是将整个对象移至虚拟函数。因此,代替

superclass->get_value(&variable_of_unknown_type);
print(variable_of_unknown_type);

您写

superclass->print_value();

现在,您无需了解子类可能包含的任何类型。

但这并不总是适当的,因为可能会有很多操作。如果您一直都在增加新的操作,那么使每个操作成为虚拟函数会很麻烦。另一方面,可能的子类的集合通常受到限制。在这种情况下,最好的选择是Visitor。可以这么说,访客将继承层次结构旋转90°。您无需修复操作集并自由添加新的子类,而是可以修复子集集并自由添加新的操作。所以代替

superclass->print_value();

您写

class PrinterVisitor : public MyVisitor
{
   virtual void processSubclass1(Subclass1* s) { print(s->double_value); }
   virtual void processSubclass2(Subclass2* s) { print(s->int_value); }
};

superclass->accept(PrinterVisitor());

现在accept是基类中唯一的virtual函数。请注意,没有强制转换可能在代码中的任何地方失败。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)&gt; insert overwrite table dwd_trade_cart_add_inc &gt; select data.id, &gt; data.user_id, &gt; data.course_id, &gt; date_format(
错误1 hive (edu)&gt; insert into huanhuan values(1,&#39;haoge&#39;); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive&gt; show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 &lt;configuration&gt; &lt;property&gt; &lt;name&gt;yarn.nodemanager.res