通用设计与奇怪的重复模板模式混合在一起。 C++

2024-03-28

考虑这样的问题。我有一个Base类和三个派生类Base。例如:DerivedA, DerivedB and DerivedC。每个派生类都有其独特的容器。因此DerivedA has std::vector<int>, DerivedB has std::set<int> and DerivedC has std::map<int, std::string>。我想要一个界面Base访问它当前指向的派生类的容器。

Base* d1 = new DerivedA;
for(std::vector<int>::iterator iter = d1->begin(); iter != d1->end(); ++iter)
{
     //processing
}

我尝试将每个容器包装为单独的类并保留其基类的指针 在里面Base class.

class CollA;

template<class T>
class traits;

template<>
class traits<CollA>
{
  public:
  typedef vector<int> container; 
};


template<class T>
class Coll
{
  public:
    typedef typename traits<T>::container container;
    typename container::iterator begin() const
    {
    }
};


class CollA : public Coll<CollA>
{
  typedef traits<CollA>::container container;
  public:
    container::iterator begin()
    {
      return V.begin();
    }
    private:
    vector<int> V;
};

class Base
{
  public:
    Base()
    {
    }
    // what to do here? I must keep a pointer to Coll; But Coll itself is a template
};

给我建议一些东西。我有点迷失在这个可怕的设计中。


为了做你想做的事,你需要定义一个公共类型的迭代器,可以从不同的返回begin() and end()在派生类中重写。

当然,在此之前,您需要决定迭代器到底要做什么,正如 Yakk 在他的评论中解释的那样。对于初学者来说,您需要决定什么value_type将通过这样的迭代器间接产生。考虑到你的三个不同的容器,我能想到的唯一常见类型是const int,作为键入std::maps are const and std::set迭代器是const迭代器(因为元素本身就是键)。因此,当使用通用迭代器类型进行迭代时,您只能观察到int在那里。

现在,迭代器实现需要根据其源自的派生类来调用不同的代码(在运行时)。这是类型擦除的典型用例。如果做得正确,这将允许您包装任何类型的迭代器,只要它支持您需要的接口。然而,就您而言,您可能不需要走那么远,因为我想您知道需要支持的完整容器集,因此迭代器类型集也是众所周知且有界的。

这意味着您可以使用boost::variant存储包装的迭代器。这应该比完整类型擦除解决方案更有效,因为它避免了一些内部虚函数调用和可能的一些堆分配(除非类型擦除解决方案可以使用某种小对象优化,这对于迭代器来说是相当可能的,但甚至是实现起来比较复杂)。

这是此类迭代器的框架实现,以及使用它的类层次结构和一些简单的测试代码。请注意,我只实现了循环工作所需的基本迭代器功能。

#include <iostream>
#include <string>
#include <vector>
#include <set>
#include <map>
#include <iterator>
#include "boost/variant.hpp"


//Helper function object types to implement each operator on the variant iterator.

struct indirection_visitor : boost::static_visitor<const int&>
{
   const int& operator()(std::vector<int>::iterator i) const { return *i; }
   const int& operator()(std::set<int>::iterator i) const { return *i; }
   const int& operator()(std::map<int, std::string>::iterator i) const { return i->first; }
};

struct prefix_increment_visitor : boost::static_visitor<>
{
   template<typename I> void operator()(I& i) const { ++i; }
};


//The iterator itself.
//It should probably hide the internal variant, in which case the non-member operators 
//should be declared as friends.

struct var_iterator : std::iterator<std::bidirectional_iterator_tag, const int>
{
   var_iterator() { }
   template<typename I> var_iterator(I i) : it(i) { }

   boost::variant<std::vector<int>::iterator, std::set<int>::iterator, std::map<int, std::string>::iterator> it;

   const int& operator*() { return boost::apply_visitor(indirection_visitor(), it); }

   var_iterator& operator++()
   {
      boost::apply_visitor(prefix_increment_visitor(), it);
      return *this;
   }
};

inline bool operator==(var_iterator i1, var_iterator i2) { return i1.it == i2.it; }
inline bool operator!=(var_iterator i1, var_iterator i2) { return !(i1 == i2); }


//Here's the class hierarchy.
//We use CRTP only to avoid copying and pasting the begin() and end() overrides for each derived class.

struct Base
{
   virtual var_iterator begin() = 0;
   virtual var_iterator end() = 0;
};

template<typename D> struct Base_container : Base
{
   var_iterator begin() override { return static_cast<D*>(this)->container.begin(); }
   var_iterator end() override { return static_cast<D*>(this)->container.end(); }
};

struct DerivedA : Base_container<DerivedA>
{
   std::vector<int> container;
};

struct DerivedB : Base_container<DerivedB>
{
   std::set<int> container;
};

struct DerivedC : Base_container<DerivedC>
{
   std::map<int, std::string> container;
};


//Quick test.

void f(Base* bp)
{
   for(auto iter = bp->begin(); iter != bp->end(); ++iter)
   {
      std::cout << *iter << ' ';
   }
   std::cout << '\n';

   //We have enough to make range-based for work too.
   for(auto i : *bp)
      std::cout << i << ' ';
   std::cout << '\n';
}

int main()
{
   DerivedA da;
   da.container = {1, 2, 3};
   f(&da);
   DerivedB db;
   db.container = {4, 5, 6};
   f(&db);
   DerivedC dc;
   dc.container = std::map<int, std::string>{{7, "seven"}, {8, "eight"}, {9, "nine"}};
   f(&dc);
}

实施注意事项:

  • 如上所述,这不是一个完整的双向迭代器;我选择该标签作为容器类型中最强大的通用迭代器。
  • 我在 C++11 模式下的 Clang 3.6.0 和 GCC 5.1.0 以及使用 boost 1.58.0 的 Visual C++ 2013 中编译并(表面上)测试了代码。
  • 该代码在 C++14 模式下也可以在上面的编译器中运行(也可以在 Visual C++ 2015 CTP6 中运行),但由于 boost 1.58 中的错误,需要进行一些小的更改(我必须报告这一点),否则你会'会出现歧义错误。您需要删除的基类indirection_visitor并让该访问者的返回类型自动确定。这仅适用于 C++14,因为它使用decltype(auto)在内部,正是这个新代码导致了歧义。早期版本的 boost 没有这个问题,但也没有返回类型的自动检测。
  • 在 C++14 模式和 boost 1.58 中,您可以使用通用 lambda 来实现简单的访问者,例如prefix_increment_visitor,这使得代码更加简单。
  • 我从代码的第一个版本中删除了比较访问者,如下所示boost::variant已经提供了一个默认的相等运算符,对于这种情况来说已经足够了(这个例子已经足够长了)。
  • 你可以加const如果需要的话,在需要的地方获得真正的 const 迭代器行为(限定begin() and end(), use static_cast<const D*>在 CRTP 中,声明要包含的变体const_iterators,调整访客)。
  • 当然,您可以实现某种穷人的变体并避免使用 boost,但是boost::variant让一切变得更简单、更清洁、更安全。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

通用设计与奇怪的重复模板模式混合在一起。 C++ 的相关文章

  • 获取 std::variant 当前持有的 typeid(如 boost::variant type())

    我已经从 boost variant 迁移到 std variant 但遇到了障碍 我在 boost type 中使用了一个很好的函数 它可以让你获取当前持有的 typeid 看https www boost org doc libs 1
  • 如何使用 MVVM 更新 WPF 中编辑的数据? [复制]

    这个问题在这里已经有答案了 我正在为聊天应用程序构建 UI 设计 在尝试更新所选联系人的消息时遇到问题 选择现有联系人 选择编辑选项 然后编辑其属性 例如用户名和图像 后 唯一进行的更改是联系人的用户名和图像 我仍然想更改 MessageM
  • 是否允许将类模板类型参数键入相同的名称?

    这似乎可以在 MSVC 中按预期编译甚至工作 但它是合法的 C 代码吗 它是否能保证执行此处所期望的操作 即将模板类型导出到结构体的同名用户 template
  • 在Application_AquireRequestState事件中用POST数据重写Url

    我有一个在其中注册路线的代码Application AcquireRequestState应用程序的事件 注册路由后 我会在 Http 运行时缓存中设置一个标志 这样我就不会再次执行路由注册代码 在此事件中注册路线有特定原因Applicat
  • 用户控件内所有控件均为空

    我有一个 UserControl 它使用 UserControl 以及其他控件 In the ascx文件我有以下代码
  • 静态类变量与外部变量相同,只是具有类作用域吗?

    在我看来 静态类变量与外部变量相同 因为你只需要declare它在static int x extern int x语句 并在其他地方实际定义它 通常在 cpp 文件中 静态类变量 h file class Foo static int x
  • 找到的程序集的清单定义与程序集引用不匹配

    我试图在 C Windows 窗体应用程序 Visual Studio 2005 中运行一些单元测试 但出现以下错误 System IO FileLoadException 无法加载文件或程序集 实用程序 版本 1 2 0 200 文化 中
  • 导出类时编译器错误

    我正在使用 Visual Studio 2013 但遇到了一个奇怪的问题 当我导出一个类时 它会抛出 尝试引用已删除的函数 错误 但是 当该类未导出时 它的行为会正确 让我举个例子 class Foo note the export cla
  • 在 MATLAB 中创建共享库

    一位研究人员在 MATLAB 中创建了一个小型仿真 我们希望其他人也能使用它 我的计划是进行模拟 清理一些东西并将其变成一组函数 然后我打算将其编译成C库并使用SWIG https en wikipedia org wiki SWIG创建一
  • 默认值 C# 类 [重复]

    这个问题在这里已经有答案了 我在控制器中有一个函数 并且我收到表单的信息 我有这个代码 public Actionresult functionOne string a string b string c foo 我尝试将其转换为类似的类
  • C#中Enum中定义的value__是什么

    What value 可能在这里 value MSN ICQ YahooChat GoogleTalk 我运行的代码很简单 namespace EnumReflection enum Messengers MSN ICQ YahooChat
  • dropdownlist DataTextField 由属性组成?

    有没有一种方法可以通过 C 使 asp net 中的下拉列表的 datatextfield 属性由对象的多个属性组成 public class MyObject public int Id get set public string Nam
  • 系统错误 124 - SHFileOperation 的 ERROR_INVALID_LEVEL

    我在使用时遇到问题SHFileOperation SHFileOperation SHFILEOPSTRUCT https stackoverflow com questions 9191415 shfileoperation shfile
  • 从事务范围调用 WCF 服务方法

    我有这样的代码 using TransactionScope scope TransactionScopeFactory CreateTransactionScope some methodes calls for which scope
  • 设计 Javascript 前端 <-> C++ 后端通信

    在我最近的将来 我将不得不制作一个具有 C 后端和 Web 前端的系统 要求 目前 我对此了解不多 我认为前端将触发数据传输 而不是后端 所以不需要类似 Comet 的东西 由于在该领域的经验可能很少 我非常感谢您对我所做的设计决策的评论
  • JavaScript - 这个这个

    String prototype foo String prototype foo bar function How can you reference the grandparent string console log this par
  • 从 C 线程调用 Python 代码

    我对从 C 或 C 线程调用 Python 代码时如何确保线程安全感到非常困惑 The Python 文档 http docs python org c api init html non python created threads似乎是
  • 在 C++ 和 Windows 中使用 XmlRpc

    我需要在 Windows 平台上使用 C 中的 XmlRpc 尽管我的朋友向我保证 XmlRpc 是一种 广泛可用的标准技术 但可用的库并不多 事实上 我只找到一个库可以在 Windows 上执行此操作 另外一个库声称 您必须做很多工作才能
  • C++ [Windows] 可执行文件所在文件夹的路径[重复]

    这个问题在这里已经有答案了 我需要访问一些文件fstream在我的 Windows 上的 C 应用程序中 这些文件都位于我的exe文件所在文件夹的子文件夹中 获取当前可执行文件的文件夹路径的最简单且更重要的 最安全的方法是什么 Use 获取
  • C# 粘贴到文本框时检查剪贴板中的字符

    有没有一些方法可以在粘贴到文本框 C 之前仅检查剪贴板中的字符 Ctrl V 和右键单击 gt 粘贴 但不使用 MaskedTextbox 在文本框文本更改中添加规则以仅接受数字 例如 private string value privat

随机推荐

  • 不使用 Math.min 查找数组中的最小值

    如何在不使用的情况下找到数组中的最小数字Math min apply var arr 5 1 9 5 7 const smallest Math min 5 1 9 5 7 console log smallest old way cons
  • 使用应用程序脚本在新的 Google 表格中插入超链接

    在当前版本的 Google Sheet 中 要将超链接插入到单元格中 您可以执行以下操作 sheet getRange row selectedColumn setValue hyperlink ontologyObject accessi
  • RX 自动完成框

    我正在尝试使用 RX 和 WPF 构建过滤器控件 所以我有一个文本框和一个列表框 启动时 列表框有 100 个联系人姓名 用户可以输入姓名来过滤列表 问题是我如何构建文本流 关键输入 然后发布 这应该是时间敏感的 所以我想只有在 750 毫
  • Flask:蓝图中的模板继承自应用程序中的模板?

    我是 Flask Jinja2 的新手 所以也许我忽略了一些明显的事情 但是 Flask 不应该开箱即用地允许蓝图中存在的模板吗 templates 文件夹来扩展由我的应用程序定义的基本模板templates 文件夹 即使蓝图还包含一个 默
  • 如何编写查询以确保电子邮件包含@

    我正在 db2 中创建一个数据库 我想添加一个约束来验证用户是否插入包含 的有效电子邮件地址 没有运气 有什么建议吗 您可以将 LIKE 与通配符一起使用 看here http publib boulder ibm com infocent
  • ASP.NET MVC3 TryValidateModel 验证整个模型集合,而不仅仅是单个实例

    我有一个需要模型列表的操作 我想同时验证每个模型与整个模型集合 我正在尝试使用 TryValidateModel 但似乎如果我的任何一个模型无效 那么所有模型都无效 我的表单显示 5 个 SurveyResponseModel 一个包含两个
  • 本机 KDE 查找 Qt 5 应用程序

    我在 Kubuntu 12 10 Linux 机器上安装了 Qt5 和 Qt4 并注意到以下内容 使用 Qt4 编译的程序具有原生 KDE 外观和感觉 我使用默认的 KDEOxygen主题 我的程序也是如此 注意带有标题的平滑渐变和聚焦小部
  • C#:有什么方法可以发现文件使用的字符集编码吗?

    有什么方法可以发现文件使用的字符集编码吗 可靠地做到这一点的唯一方法是寻找字节顺序标记 http en wikipedia org wiki Byte order mark在文本文件的开头 此 blob 更一般地表示所使用的字符编码的字节顺
  • 实现3d索贝尔算子

    我目前正在研究从包含体素的 MRI 数据量中去除不均匀性 我想在这些体积上应用索贝尔运算符来找到梯度 我熟悉二维索贝尔掩模和二维图像的邻域 索贝尔面具 1 2 1 0 0 0 1 2 1 1 0 1 2 0 2 1 0 1 x y 的邻域
  • Websocket 标头丢失

    我正在 Rails 3 2 13 中使用 faye gem 在开发过程中 我在 localhost 9292 上运行 faye 在 localhost 3000 上运行我的应用程序 我可以使用命令行中的curl 请求激活弹出窗口 但无法从应
  • 未找到 React 应用程序的 GitHub Pages 页面

    我正在尝试将我的单页网站发布到 github 页面 但遇到了问题 当我转到网站链接时 出现 404 错误并显示以下消息 找不到文件 此地址配置的站点不包含请求的文件 如果这是您的站点 请确保文件名大小写正确匹配 URL 对于根 URL 例如
  • 如何显示wordpress中的所有类别?

    我使用了这段代码 categories wp get post categories get the ID foreach categories as category echo div class col md 4 a href get
  • python topN 最大堆,使用 heapq 还是自己实现?

    python中有heapq 用于一般用途 我想记录topN 0 20 10e7 条记录 如果使用heapq 应该使用 将最大值转换为最小值 并记录底部的最小数量 以调用 heapq heappushpop 我应该使用 heapq 还是自行实
  • Recyclerview:监听padding点击事件

    我有一个水平的RecyclerView with leftPadding 48dp topPadding 24dp and clipToPadding false 它从左侧的空白空间开始 但是当用户滚动列表时 其项目将绘制在该 以前是空的
  • 在iOS AVPlayer中,addPeriodicTimeObserverForInterval似乎丢失了

    我正在尝试设置AVPlayer addPeriodicTimeObserverForInterval 有人用过这个成功吗 我正在使用 Xcode 8 1 Swift 3 接受的答案让人感觉您可以将返回值分配给局部变量并忽略它 但根据doc
  • getPass() 在 Eclipse 中回显密码

    嘿 大家刚刚开始涉足一点 Python 以帮助处理工作中的一些脚本 但我似乎被一个小问题所困扰 我使用 Eclipse 和 pydev 作为我的开发平台 并针对 python 2 7 3 进行开发 我只是想提示用户输入密码 使用 passw
  • MKMapView 显示错误保存的区域

    当我的 iPhone 应用程序关闭时 我将地图区域保存到用户默认值中 如下所示 MKCoordinateRegion region mapView region NSUserDefaults standardUserDefaults set
  • 一个 INSERT 多个 SELECT

    我已经读过this https stackoverflow com questions 20426967 adding two select statements into one insert into statement in post
  • 如何对齐标签和输入字段

    我希望我的输入字段 标题 直接呈现在单选按钮下方 但现在它离左侧太远了 我需要额外的div因为它是动态表单 但我想要网格视图 我怎样才能做到这一点
  • 通用设计与奇怪的重复模板模式混合在一起。 C++

    考虑这样的问题 我有一个Base类和三个派生类Base 例如 DerivedA DerivedB and DerivedC 每个派生类都有其独特的容器 因此DerivedA has std vector