【C++泛型编程】模板偏特化、局部类和型别映射(Int2Type,Type2Type)以及型别选择

2023-11-06

1.模板偏特化

         模板偏特化是让你在template的所有可能实体中特化出一组子集。
下面是一个模板全特化的例子,假设有一个类模板,名为Widget:
template<class Window,class Controller>
class Widget
{
....各种操作.....
};
特化的情况如下:
template<>
class Widget<ModalDialog,MyController>
{
...各种特化后的操作...
};
其中ModalDialog,MyController是另外定义的类。

有时候想针对任意的Window并搭配固定的MyController来特化Widget,这时候就需要模板偏特化机制:
template<class Window>            //Window仍然是泛化
class Widget<Window,MyController> // MyController是特化
{
.....;
};
偏特化的特性非常强大,当你具现化一个template时,编译器会把目前存在的偏特化和全特化作比较,并找出最匹配的。这种偏特化机制不能用在函数身上(不管是否为成员函数)。
注:a.你可以全特化class template中的成员函数,但是不能偏特化。
    b.你不能偏特化namespace-level(非成员函数)函数。(可以运用Int2Type和Type2Type工具实现

  template<class T,class U> T Fun(U obj);//模板函数
  template<class U> void Fun<void,U>(U obj)//错误,不能偏特化
  template<class T> T Fun(Window obj);  //正确,是函数重载


2.局部类
       局部类可以定义如下:

void Fun()
{
class Local
{
...member variables...
};
...code using Local...
};

        局部类不能定义static成员变量,也不能访问非static局部变量。局部类可以在template函数中使用。定义于template函数内的局部类可以使用函数的template参数作为其成员变量。
如下一个例子:有一个MakerAdapter 模板函数,可以将某个接口转接为另一个接口。它是在局部类的辅助下完成这一个接口的转换,这个局部类有泛化型别的成员。

class Interface
{
pubic:
 virtual void Fun()=0;
};
template<class T,class P>
Interface * MakeAdapter(const T& obj,const P& arg)
{
class Local:public Interface
{
public:
 Local(const T& obj,const P& arg):obj_(obj),arg_(arg){};
virtual void Fun()
{
obj_.Call(arg_);
}
private:
T obj_;
P arg_;
};
return new Local(obj,arg);
}


任何使用局部类的地方,都可以改用函数外的模板类来完成,并不一定要使用local class.但是局部类可以提高符号的地域性。外界不能继承一个隐藏在函数内的类。

3.常整数映射为型别(Int2Type)
 

 template<int v>
 struct Int2Type
 {
  enum{value=v};
};

     Int2Type会根据参数v来产生不同的型别。这是因为不同的template具现体就是不同的型别。如Int2Type<0>和Int2Type<1>是不同的型别。这样一来就可以根据编译期计算出来的结果选用不同的函数,可以运用这个常数达到静态分派。
 使用Int2Type的两个条件:
          a.有必要根据某个编译期常数调用一个或多个不同的函数。
          b.有必要在编译期实施分派。
  若打算在执行期进行分派可以使用if-else或swith语句。大部分时候他们的执行成本是微不足道,但是有时候你不能那么做,因为if-else语句要求每一个分支都得编译成功,即使该条件测试在编译期
才知道。

  下面是错误的代码:因为型别T没有Clone()函数,就会编译出错

template <typename T, bool isPolymorphic>
class MyContainer
{
public:
    void DoSomething( T* p)
    {
        if ( isPolymorphic )
        {
            T *newPtr = p->Clone();
            // ...
        }
        else
        {
            T *newptr = new T(*p);
            // ...
        }
    }
    // ...
}


最好的解决办法是利用Int2Type进行函数重载

  template <typename T, bool isPolymorphic>
class MyContainer
{
private:
    void DoSomething( T* p, Int2Type<true>)
    {
        T* newptr = p->Clone();
        // ...
    }
    void DoSomething( T* p, Int2Type<false>)
    {
        T* newptr = new T(*p);
        // ...
    }
public:
    void DoSomething( T* p)
    {
        DoSomething( p, Int2Type<isPolymorphic>());
    }
};


这个小技巧之所以有用,是编译器并不会去编译一个未被使用到的template函数。只会对它做文法检查。

4.型别对型别的映射(Type2Type)

       由于不存在template函数的偏特化,如果想模拟出类似的机制怎么办呢?
如下的程序:

template <class T, class U>
T *Create(const U& arg)
{
    return new T(arg);
}

现在假设Widget对象是你碰不到的老代码,它需要两个参数才能构造出对象来,第二个参数固定为-1.如果派生类则没有这个问题。
现在该如何特化Create(),让它处理独特的Widget呢? 一个明显的方案是写出一个CreateWidget()来专门处理,但是这样就没有一个统一的接口来生成Widgets和其派生对象。
由于无法偏特化一个函数,下面的写法也是错误的:
template <class U>
Widget *Create<Widget, U>(const U& arg)
{
    return new Widget(arg, -1);
}

由于函数缺乏偏特化机制,因此只能用重载的方式实现:

template <class T, class U>
T *Create( const U& arg, T) // T is dummy
{
    return new T(arg);
}

template <class U>
Widget *Create( const U& arg, Widget)   // Widget is dummy
{
    return new Widget(arg,-1);
}


问题:但是这种解法会很轻易构造未被使用的复杂对象,造成额外开销。这是我们可以使用Type2Type来解决,它的定义如下:

template <typename T>
struct Type2Type
{
    typedef T OriginalType;
};

template <class T, class U>
T *Create( const U& arg, TypeToType<T>)
{
    return new T(arg);
}
template <class U>
Widget *Create( const U& arg, TypeToType<Widget>)
{
    return new Widget(arg, -1);
}

String *pS = Create("Hello", Type2Type<String>());  
Widget *pW = Create( 200, Type2Type<Widget>());

Create()的第二个参数只是用来选择适当的重载函数,可以令各种Type2Type实体对应程序中的各种型别,并根据不同的Type2Type实体来特化Create().

5.型别选择

     有时候泛型程序中需要根据一个bool变量来选择某个型别或另一个型别。
 在MyContainer的例子中,你可能会以一个std::vector作为存储结构,面对多态型别,你不能存储对象实体,只能存储指针。对于非多态型别,可以存储实体,这样比较有效率。

template <typename T, bool isPolymorphic>
class MyContainer
{
    // store pointers in polymorphic case: vector<T *>
    // store values otherwise: vector<T>
};

你需要根据isPolymorphic来决定ValueType定义为T *还是T.可以使用如下Traits 类模板的方法来定义:

template <typename T, bool isPolymorphic>
struct MyContainerValueTraits
{
    typedef T* ValueType;
};
template <typename T>
struct MyContainerValueTraits< T, false>
{
    typedef T ValueType;
};
template <typename T, bool isPolymorphic>
struct MyContainer
{
    typedef MyContainerValueTraits<T,isPolymorphic> Traits;
    typedef typename Traits::ValueType ValueType;
    // ...
    vector<ValueType>   v;
};


 问题:上面的做法其实很笨拙难用,此外也无法扩充:针对不同的型别的选择,你必须定义出专属的Traits类模板。
Loki库中提供了Select 类模板可以使型别的选择立时可用。它采用偏特化机制:

template <bool Flag, typename T, typename U>
struct Select
{
    typename T Result;
};
template <typename T, typename U>
struct Select<false, T, U>
{
    typename U Result;
};

其运作方式是:如果Flag为True,编译器会使用第一份泛型定义,因此Result会被定义成T.如果Flag为False.那么偏特化机制会运作,于是Result被定义为U.现在可以很方便的定义 MyContainer::ValueType了。

template <typename T, bool isPolymorphic>
struct MyContainer
{
    typedef typename Select<isPolymorphic, T*, T>::Result ValueType;
    // ...
    vector<ValueType>   v;
};


 

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

【C++泛型编程】模板偏特化、局部类和型别映射(Int2Type,Type2Type)以及型别选择 的相关文章

  • 为什么 int8_t 和用户通过 cin 输入显示奇怪的结果[重复]

    这个问题在这里已经有答案了 一小段代码让我发疯 但希望你能阻止我跳出窗外 看这里 include
  • 如何将非静态类成员“std::bind”绑定到 Win32 回调函数“WNDPROC”?

    我正在尝试将非静态类成员绑定到标准WNDPROC http msdn microsoft com en us library ms633573 aspx功能 我知道我可以通过将类成员设为静态来简单地做到这一点 但是 作为一名 C 11 ST
  • 确保 StreamReader 不会挂起等待数据

    下面的代码读取从 tcp 客户端流读取的所有内容 并且在下一次迭代中它将仅位于 Read 上 我假设正在等待数据 我如何确保它不会在没有任何内容可供读取时返回 我是否必须设置低超时 并在失败时响应异常 或者有更好的办法吗 TcpClient
  • 提交后禁用按钮

    当用户提交付款表单并且发布表单的代码导致 Firefox 中出现重复发布时 我试图禁用按钮 去掉代码就不会出现这个问题 在firefox以外的任何浏览器中也不会出现这个问题 知道如何防止双重帖子吗 System Text StringBui
  • MVC 在布局代码之前执行视图代码并破坏我的脚本顺序

    我正在尝试将所有 javascript 包含内容移至页面底部 我正在将 MVC 与 Razor 一起使用 我编写了一个辅助方法来注册脚本 它按注册顺序保留脚本 并排除重复的内容 Html RegisterScript scripts som
  • 错误:表达式不产生值

    我尝试将以下 C 代码转换为 VB NET 但在编译代码时出现 表达式不产生值 错误 C Code return Fluently Configure Mappings m gt m FluentMappings AddFromAssemb
  • 在 C 中匹配二进制模式

    我目前正在开发一个 C 程序 需要解析一些定制的数据结构 幸运的是我知道它们是如何构造的 但是我不确定如何在 C 中实现我的解析器 每个结构的长度都是 32 位 并且每个结构都可以通过其二进制签名来识别 举个例子 有两个我感兴趣的特定结构
  • 单个对象的 Monogame XNA 变换矩阵?

    我读过一些解释 XNA Monogame 变换矩阵的教程 问题是这些矩阵应用于 SpriteBatch Begin matrix 这意味着所有 Draw 代码都将被转换 如何将变换矩阵应用于单个可绘制对象 就我而言 我想转换滚动背景 使其自
  • 获取两个工作日之间的天数差异

    这听起来很简单 但我不明白其中的意义 那么获取两次之间的天数的最简单方法是什么DayOfWeeks当第一个是起点时 如果下一个工作日较早 则应考虑在下周 The DayOfWeek 枚举 http 20 20 5B1 5D 3a 20htt
  • 回发后刷新时提示确认表单重新提交。我做错了什么?

    我有一个以空白 默认状态启动的仪表板 我让用户能够将保存的状态加载到仪表板中 当他们单击 应用 按钮时 我运行以下代码 function CloseAndSave var radUpload find radUpload1ID var in
  • 由 IHttpClientFactory 注入时模拟 HttpClient 处理程序

    我创建了一个自定义库 它会自动为依赖于特定服务的 Polly 策略设置HttpClient 这是使用以下方法完成的IServiceCollection扩展方法和类型化客户端方法 一个简化的例子 public static IHttpClie
  • 我可以使用 moq Mock 来模拟类而不是接口吗?

    正在经历https github com Moq moq4 wiki Quickstart https github com Moq moq4 wiki Quickstart 我看到它 Mock 一个接口 我的遗留代码中有一个没有接口的类
  • 如何禁用 fread() 中的缓冲?

    我正在使用 fread 和 fwrite 读取和写入套接字 我相信这些函数用于缓冲输入和输出 有什么方法可以在仍然使用这些功能的同时禁用缓冲吗 Edit 我正在构建一个远程桌面应用程序 远程客户端似乎 落后于服务器 我不知道可能是什么原因
  • CMake 无法确定目标的链接器语言

    首先 我查看了this https stackoverflow com questions 11801186 cmake unable to determine linker language with c发帖并找不到解决我的问题的方法 我
  • “接口”类似于 boost::bind 的语义

    我希望能够将 Java 的接口语义与 C 结合起来 起初 我用过boost signal为给定事件回调显式注册的成员函数 这非常有效 但后来我发现一些函数回调池是相关的 因此将它们抽象出来并立即注册所有实例的相关回调是有意义的 但我了解到的
  • 动态添加 ASP.Net 控件

    我有一个存储过程 它根据数据库中存储的记录数返回多行 现在我想有一种方法来创建 div 带有包含该行值的控件的标记 如果从数据库返回 10 行 则 10 div 必须创建标签 我有下面的代码来从数据库中获取结果 但我不知道如何从这里继续 S
  • 使用 %d 打印 unsigned long long

    为什么我打印以下内容时得到 1 unsigned long long int largestIntegerInC 18446744073709551615LL printf largestIntegerInC d n largestInte
  • 使用 C# 读取 Soap 消息

  • 无法接收 UDP Windows RT

    我正在为 Windows 8 RT 编写一个 Windows Store Metro Modern RT 应用程序 需要在端口 49030 上接收 UDP 数据包 但我似乎无法接收任何数据包 我已按照使用教程进行操作DatagramSock
  • 当我使用 OpenSSL1.1.0g 根据固定的 p 和 g 值创建 Diffie Hellman 密钥协议密钥时,应该执行哪些检查?

    您好 我尝试通过这段代码使用修复 p 和 g 参数来制作 Diffie Hellman Keysanswer https stackoverflow com a 54538811 4706711 include

随机推荐

  • 使用ant-design-pro-vue时,将登录从mock换成后台出现问题(请求出现错误,请稍后再试)

    因为我不是专门的前端 所以搞这个时 困难重重 在使用这个模板时 我想要将登录换成从后台进行响应 在一开始我是通过项目中的mock文件下的auth js来找到对应的响应数据 看到如上图所示 我心想 稳了 结果我后台就按照这个来造了一个测试 结
  • 孔板流量计计算公式_差压式流量计常用计算公式及计算实例

    差压式流量计维修中计算工作是仪表人不可缺少的 在本文分享差压式流量计常用流量计算公式及计算实例 掌握这些干货技术 对用好差压式流量计有事半功倍效果 流量计算公式1 差压式流量计的差压与流量关系的换算差压式流量计的差压与流量的平方成正比 或者
  • 【Django快速开发实战】(30~51)使用Django创建一个基础应用:职位管理系统

    30 遗留系统集成 为已有系统数据库生成管理后台 流程说明 为已有数据库生成管理后台 创建项目和应用 创建新项目 django admin startproject empmanager 创建新应用 django admin startap
  • 基于idea-学生管理系统(list),包含五门课的成绩。

    学生的类 有学生的性别 籍贯 姓名和五门课程的成绩 用private进行封装 同时写出getter和setter方法 package com zheng people public class Student private String
  • Redis学习笔记9:主从复制

    一 主从复制是什么 主从复制 是指将一台Redis服务器的数据 复制到其他的Redis服务器 前者称为主节点 master leader 后者称为从节点 slave follower 数据的复制是单向的 只能由主节点到从节点 Master以
  • java和C#在嵌套类上的差异

    平时不太用java的inner class 也就知道里面inner class可以访问外部对象的实例成员 今天碰巧试了一下C 和java的嵌套类的区别 感觉java的嵌套类其实更像是嵌套对象的样子 而C 的嵌套类则就是类定义的嵌套的感觉只可
  • 使用supervisor使Laravel的queue保持后台常驻

    我的个人博客 逐步前行STEP 一 安装supervisor 1 yum install python setuptools 2 easy install supervisor 二 配置supervisor 1 echo superviso
  • 离10000小时的差距

    03年之前没有 看到程序就烦 03年 04年 ASP写网站 打下一点程序基础 对目前影响应该在100小时左右 05年下半年 06年上半年 学WIN32程序设计 DX等 折合每天3小时到4小时 大约1000小时 06年工作原因 基本可以忽略
  • windows10上传文件到vmware虚拟机

    网上看了很多的方案 有共享文件夹的 也有安装vmware tools的 都试过了不成功 后来使用的ftp工具上传 先使用 ip addr 看虚拟机的IP地址 第二条ens33下面的地址 我本机安装的filezilla 然后远程 默认端口22
  • ELI5:导数,偏导数

    导数 导数就是描述某个事物的变化速率 举个最常见的例子 当人从某地移动到另一地点的时候 速度就是这个移动的导数 因为它描述了移动的变化速率 再继续看 加速度就是速度的导数 因为加速度描述了速度的变化速率 当加速度恒定的时候 我们可以想到 速
  • href 属性 和 target属性

  • 悟空浏览器——青龙羊毛

    青龙羊毛之元宵特辑 六 今日中青看点又迎来了一波黑号 本菜鸡再次中奖 非酋体质 每次必中招 啥也不说了 发个新毛吧 悟空浏览器之前毛毛很多 有个群友和我说过 现在那位群友都褪裙了 毛毛才有脚本 猴哥 猴哥浏览器也是头条系的 不知道能坚持多久
  • 初等代数不等式2

    表示共有个参数的所有积之和 共有项 第个参数的指数是 第个参数的指数是 故 表示共有个参数的所有积之和 共有项 第个参数的指数是 第个参数的指数是 第个参数的指数是 故 即 表示共有个参数的所有积之和 共有项 第个参数的指数是 第个参数的指
  • 【JDK】二、环境变量从jdk17切换为jdk8后不生效的解决办法

    环境变量从jdk17切换为jdk8后不生效的解决办法 一 问题描述 二 环境变量为java17时的截图 三 修改为java8时的截图 四 解决办法 1 原因分析 2 删除jdk17和jdk8默认的配置或者把默认的下移 统一使用自己的 JAV
  • 浅谈vlan中pvid的作用

    vlan中的pvid其实就是处理标签的一种方式 在端口为access模式的时候pvid access端口本身的vlan 也无法单独配置 在端口为trunk的时候pvid是可以根据需求配置的 默认的pvid vlan1 而在trunk端口模式
  • GIT使用(踩坑)

    1 关于远程路径的设置 双反斜线 和 单斜线 的问题 这样写可以 git remote add origin L BottleCapDetection 这样写也可以 git remote add origin L BottleCapDete
  • 如何将本地文件上传至阿里云ECS中

    本人尝试利用SSH Secure Shell Client软件登录阿里云ECS 发现并无法登录 但尝试后发现WinSCP可以对ECS的文件进行操作 偷懒不想使用ssh命令上传文件 同时可视化操作会安心一些 具体操作如下 第一步 开启阿里云E
  • 在IDEA中配置Git

    文章目录 在IDEA中配置Git 开发中IDEA的Git常见操作 初始化并提交项目到远程仓库 项目leader 1 在GitHub中创建远程仓库 2 将maven工程交给Git管理 3 配置忽略文件 4 提交到本地仓库 5 推送到远程仓库
  • react 之 styled-components

    css样式并不像JavaScript语法有作用域 因此css样式作用于全局很容易造成全局污染 为了防止这类问题发生 我们可以遵循CSS的BEM规范 即 blockName elementName modifierName 模块名 元素名 修
  • 【C++泛型编程】模板偏特化、局部类和型别映射(Int2Type,Type2Type)以及型别选择

    1 模板偏特化 模板偏特化是让你在template的所有可能实体中特化出一组子集 下面是一个模板全特化的例子 假设有一个类模板 名为Widget template