避免打开模板参数

2023-11-29

简化后我有以下类层次结构:

class BaseVec {
  public:
    BaseVec() {};
    virtual ~BaseVec() {};

    virtual double get_double(int i) const = 0;
};

template<typename T>
class Vec : public BaseVec {
  public:
    Vec() { ... };
    ~Vec() { ... };

    T get(int i) const { ... };

    double get_double(int i) const {
      return get(i);
    };
};

在我的项目中,我反复得到以下形式的代码:

template<typename T>
double foo_template(void* p) {
  Vec<T>* v = reinterpret_cast<Vec<T>*>(p);
  // use v which involves calling get
  return result;
}

double foo(void* p, int type) {
  if (type == 0) {
    return foo_template<double>(p);
  } else if (type == 1) {
    return foo_template<int>(p);
  } else if (...) {
    // etc.
  } else {
    //unsupported type
  }
}

(我可以使用开关并使用枚举,或者首先进行强制转换p to BaseVec然后做dynamic_casts,但逻辑保持不变)

这并不理想维护。例如,当我添加一个想要支持的附加类时,我必须向每个 if-else-if 块添加一个子句。

简化此操作的一种可能方法是强制转换p to BaseVec*并使用get_double方法。但是,由于经常调用此方法,因此会导致性能不佳。此外,这并不总是可能的:有时我想打电话给get方法作为返回的类型很重要。

我尝试了访问者模式,尽管这有一些优点,但这仍然意味着我必须为每个可能的模板参数编写单独的代码。

有什么方法可以让这段代码更容易维护吗?

PS:我对发生的事情没有(太多)控制权foo. foo由外部程序(确切地说是 R)调用。因此,我只能将通用指针、int、double 和字符向量传递给foo.

PPS:也欢迎提出更好的标题建议。


首先,不要使用reinterpret_cast在与多态类的指针之间进行转换时。您可以编写一个简单的指针包装器,它允许您使用安全转换运算符static_cast:

template <class Type>
class PointerWrapper
{
public:

    PointerWrapper(Type* object);
    PointerWrapper& operator=(Type* object);
    Type* operator->();

protected:

    Type* object;

};

template <class Type>
PointerWrapper<Type>::PointerWrapper(Type* object) :
    object(object)
{
}

template <class Type>
PointerWrapper<Type>& PointerWrapper<Type>::operator=(Type* object)
{
    this->object = object;
}

template <class Type>
Type* PointerWrapper<Type>::operator->()
{
    return object;
}

现在你可以写:

typedef PointerWrapper<BaseVec> BaseVecPointer;

template<typename T>
double foo(void* p) {
    BaseVecPointer* vp = static_cast<BaseVecPointer*>(p);
    // ...
    // ... = (*vp)->get_double(...);
    // ...
    return result;
}

在此代码中使用了多态性功能,即函数get_double被调用而不是调用get.

但如果你想打电话get, not get_double,即您想根据运行时变量的值调用具有不同模板参数的模板函数,可以使用以下方法:

enum FooTypes
{
    NoFooType = -1,
    DoubleFooType = 0,
    IntegerFooType = 1,
    // ...
    FooTypesCount
};

template<FooTypes fooType>
struct ChooseType
{
    static
    const FooTypes value = NoFooType;

    typedef void Type;
};

template<>
struct ChooseType<DoubleFooType>
{
    static
    const FooTypes value = DoubleFooType;

    typedef double Type;
};

template<>
struct ChooseType<IntegerFooType>
{
    static
    const FooTypes value = IntegerFooType;

    typedef int Type;
};

在这里您应该编写类模板的专业化ChooseType对于所有可能的值type多变的。 下面的代码描述了该功能ChooseFoo选择什么专业foo_template函数模板应该被调用:

typedef double (*FooFunction)(void*);

template<FooTypes fooType>
FooFunction ChooseFooImpl(int type)
{
    if (type == fooType)
    {
        if (ChooseType<fooType>::value != NoFooType)
        {
            return foo_template<typename ChooseType<fooType>::Type>;
        }
        else
        {
            return NULL;
        }
    }
    else
    {
        return ChooseFooImpl<(FooTypes)(fooType - 1)>(type);
    }
}

template<>
FooFunction ChooseFooImpl<NoFooType>(int type)
{
    return NULL;
}

FooFunction ChooseFoo(int type)
{
    return ChooseFooImpl<FooTypesCount>(type);
}

这是foo功能实现:

double foo(void* p, int type)
{
    FooFunction fooFunction = ChooseFoo(type);

    if (fooFunction != NULL)
    {
        return fooFunction(p);
    }
    else
    {
        //unsupported type
        // ...
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

避免打开模板参数 的相关文章

  • 在 LINQ 查询中返回不带时间的日期

    我正在编写一个查询 我想计算按日期联系我们的呼叫中心的次数 看起来很简单 但由于联系日期字段是日期时间字段 我得到了时间 因此当我按联系日期 时间 分组时 每个联系日期实例的计数为 1 所以 我想只按日期分组 而不按时间分组 下面是我用来查
  • 自动从 C# 代码进行调试过程并读取寄存器值

    我正在寻找一种方法来读取某个地址的 edx 注册表 就像这个问题中所问的那样 读取eax寄存器 https stackoverflow com questions 16490906 read eax register 虽然我的解决方案需要用
  • FFMPEG Seeking 带来音频伪影

    我正在使用 ffmpeg 实现音频解码器 在读取音频甚至搜索已经可以工作时 我无法找到一种在搜索后清除缓冲区的方法 因此当应用程序在搜索后立即开始读取音频时 我没有任何工件 avcodec flush buffers似乎对内部缓冲区没有任何
  • 使用 Microsoft Graph API 订阅 Outlook 推送通知时出现 400 错误请求错误

    我正在尝试使用 Microsoft Graph API 创建订阅以通过推送通知获取 Outlook 电子邮件 mentions 我在用本文档 https learn microsoft com en us graph api subscri
  • 如何在我的应用程序中使用 Windows Key

    Like Windows Key E Opens a new Explorer Window And Windows Key R Displays the Run command 如何在应用程序的 KeyDown 事件中使用 Windows
  • 为什么 POSIX 允许在只读模式下超出现有文件结尾 (fseek) 进行搜索

    为什么寻找文件结尾很有用 为什么 POSIX 让我们像示例中那样在以只读方式打开的文件中进行查找 c http en cppreference com w c io fseek http en cppreference com w c io
  • 将字符串从非托管代码传递到托管

    我在将字符串从非托管代码传递到托管代码时遇到问题 在我的非托管类中 非托管类 cpp 我有一个来自托管代码的函数指针 TESTCALLBACK FUNCTION testCbFunc TESTCALLBACK FUNCTION 接受一个字符
  • 基于范围的 for 循环中的未命名循环变量?

    有没有什么方法可以不在基于范围的 for 循环中 使用 循环变量 同时也避免编译器发出有关未使用它的警告 对于上下文 我正在尝试执行以下操作 我启用了 将警告视为错误 并且我不想进行像通过在某处毫无意义地提及变量来强制 使用 变量这样的黑客
  • 按字典顺序对整数数组进行排序 C++

    我想按字典顺序对一个大整数数组 例如 100 万个元素 进行排序 Example input 100 21 22 99 1 927 sorted 1 100 21 22 927 99 我用最简单的方法做到了 将所有数字转换为字符串 非常昂贵
  • Windows 窗体不会在调试模式下显示

    我最近升级到 VS 2012 我有一组在 VS 2010 中编码的 UI 测试 我试图在 VS 2012 中启动它们 我有一个 Windows 窗体 在开始时显示使用 AssemblyInitialize 属性运行测试 我使用此表单允许用户
  • 编译的表达式树会泄漏吗?

    根据我的理解 JIT 代码在程序运行时永远不会从内存中释放 这是否意味着重复调用 Compile 表达式树上会泄漏内存吗 这意味着仅在静态构造函数中编译表达式树或以其他方式缓存它们 这可能不那么简单 正确的 他们可能是GCed Lambda
  • Ruby 模板:如何将变量传递到内联 ERB 中?

    我有一个内联到 Ruby 代码中的 ERB 模板 require erb DATA a gt HELLO b gt WORLD template ERB new lt lt EOF current key is current value
  • 初始化变量的不同方式

    在 C 中初始化变量有多种方法 int z 3 与 int 相同z 3 Is int z z 3 same as int z z 3 您可以使用 int z z 3 Or just int z 3 Or int z 3 Or int z i
  • .NET 选项将视频文件流式传输为网络摄像头图像

    我有兴趣开发一个应用程序 它允许我从 xml 构建视频列表 包含视频标题 持续时间等 并将该列表作为我的网络摄像头流播放 这意味着 如果我要访问 ustream tv 或在实时通讯软件上激活我的网络摄像头 我的视频播放列表将注册为我的活动网
  • 将应用程序从 Microsoft Access 迁移到 VB 或 C#.NET

    我目前正试图说服管理层需要将我们的应用程序之一移植到 NET 该应用程序已经发展成为 Access 中的一个庞然大物 SQL 后端 拥有 700 个链接表 650 个表单 子表单 130 个模块和 850 个查询 我几乎知道这样做的所有主要
  • char指针或char变量的默认值是什么[重复]

    这个问题在这里已经有答案了 下面是我尝试打印 char 变量和指针的默认值 值的代码 但无法在控制台上看到它 它是否有默认值或只是无法读取 ASCII 范围 include
  • 如何构建印度尼西亚电话号码正则表达式

    这些是一些印度尼西亚的电话号码 08xxxxxxxxx 至少包含 11 个字符长度 08xxxxxxxxxxx 始终以 08 开头 我发现这个很有用 Regex regex new Regex 08 0 9 0 9 0 9 0 9 0 9
  • 如何使用 ReactiveList 以便在添加新项目时更新 UI

    我正在创建一个带有列表的 Xamarin Forms 应用程序 itemSource 是一个reactiveList 但是 向列表添加新项目不会更新 UI 这样做的正确方法是什么 列表定义 listView new ListView var
  • C++ 成员函数中的“if (!this)”有多糟糕?

    如果我遇到旧代码if this return 在应用程序中 这种风险有多严重 它是一个危险的定时炸弹 需要立即在应用程序范围内进行搜索和销毁工作 还是更像是一种可以悄悄留在原处的代码气味 我不打算writing当然 执行此操作的代码 相反
  • 不同类型的指针可以互相分配吗?

    考虑到 T1 p1 T2 p2 我们可以将 p1 分配给 p2 或反之亦然吗 如果是这样 是否可以不使用强制转换来完成 或者我们必须使用强制转换 首先 让我们考虑不进行强制转换的分配 C 2018 6 5 16 1 1 列出了简单赋值的约束

随机推荐