C++模板基础(六)

2023-11-19

类模板与成员函数模板
● 使用 template 关键字引入模板: template class B {…};
– 类模板的声明与定义 翻译单元的一处定义原则

template<typename T>
class B; //类模板的声明

template<typename T>
class B //类模板的定义
{

};

template<typename T>
class B //类模板必须满足翻译单元级别的一处定义原则,Error: Redefinition of 'B'
{

};

– 成员函数只有在调用时才会被实例化

template<typename T>
class B //类模板的定义
{
public:
    void fun(T input)
    {
        std::cout << input << std::endl;
    }
};
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    B<int> x;
    x.fun(3);
    return a.exec();
}

在这里插入图片描述

template<typename T>
class B
{
public:
    void fun(T input)
    {
        std::cout << input << std::endl; //#1: Error: main.cpp:9:19: Invalid operands to binary expression ('std::ostream' (aka 'basic_ostream<char, char_traits<char>>') and 'Str')
    }
};

struct Str{};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    B<Str> y; //OK
    y.fun(Str{}); //该语句引发了#1处的报错,证明了成员函数只有在调用时才会被实例化
    return a.exec();
}

– 类内类模板名称的简写

template<typename T>
class B
{
public:
    auto fun()
    {
        return B<T>{}; //OK
        //如果该语句简写为return B{};,那么编译器自动视它为return B<int>{};
        //如果模板参数为template<typename T1, typename T2> class B;,那么编译器自动视return B{};为return B<T1, T2>{};
    }
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    B<int> x;
    x.fun(); //OK
    
    return a.exec();
}

– 类模板成员函数的定义(类内、类外)

template<typename T>
class B
{
public:
    void fun(); //类模板成员函数的声明
};

template<typename T>
void B<T>::fun() //OK, 规范类模板成员函数的类模板外定义
{

}

void B<T>::fun() //Oops, 错误的类模板成员函数的类模板外定义
{

}

● 成员函数模板
– 类的成员函数模板

class B
{
public:
    template <typename T>
    void fun() //类的成员函数模板的类内定义
    {
        std::cout << "template <typename T> void fun()\n";
    }
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    B x;
    x.fun<int>();

    return a.exec();
}

在这里插入图片描述

class B
{
public:
    template <typename T>
    void fun(); //类的成员函数模板的类内声明
};

template<typename T>
void B::fun() //类的成员函数模板的类外定义
{
    std::cout << "template <typename T> void fun()\n";
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    B x;
    x.fun<int>();

    return a.exec();
}

– 类模板的成员函数模板

template<typename T> //#1
class B
{
public:
    template <typename T> //#2, 隐藏了#1处的T, Warning: Declaration of 'T' shadows template parameter
    void fun()
    {
        T  //是#1处的T还是#2处的T?
    }
    
private:
    T m_data; //#1处的T, OK
};
template<typename T>
class B
{
public:
    template <typename T2>
    void fun() //类模板的成员函数模板的类内定义
    {
        T2 tmp1; //OK
        T tmp2; //OK
    }
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    B<int> x;
    x.fun<float>(); //OK

    return a.exec();
}
template<typename T>
class B
{
public:
    template <typename T2>
    void fun(); //类模板的成员函数模板的类内声明
};

template<typename T>
template <typename T2>
void B<T>::fun() //类模板的成员函数模板的类外定义
{
    T2 tmp1; //OK
    T tmp2; //OK
}

用google搜索引擎搜索gcc github vector可以搜到vector的实现

● 友元函数(模板)

template <typename T2>
void fun(); //友元函数模板的声明

template<typename T>
class B
{
    template <typename T2>
    friend void fun(); //声明该函数模板为类的模板的友元

    int x;
};

template <typename T2>
void fun() //定义
{
    B<int> tmp1;
    tmp1.x; //OK

    B<char> tmp2;
    tmp2.x; //OK
}

– 可以声明一个函数模板为某个类(模板)的友元

template<typename T>
class B
{
    friend void fun(B input) //被编译器解析为B<T>
    {
        std::cout << input.x << std::endl;
    }

    int x = 3;
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    B<int> val;
    fun(val);

    B<float> val2;
    fun(val2);

    return a.exec();
}
template<typename T>
class B
{
    friend auto operator + (B input1, B input2) //由#1,被视为B<int>,是B<int>的友元函数,但不是B<float>的友元函数
    {
        B res;
        res.x = input1.x + input2.x;

        B<float> tmp; //
        tmp.x; //Error: 'x' is a private member of 'B<float>'

        return res;
    }

    int x = 3;
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    B<int> val1;
    B<int> val2;
    B<int> res = val1 + val2; //#1,此处用int实例化

    return a.exec();
}

– C++11 支持声明模板参数为友元

template<typename T>
class B
{
    friend T; //OK Since C++11
};

● 类模板的实例化 (class_template)
– 与函数实例化很像
– 可以实例化整个类,或者类中的某个成员函数
● 类模板的(完全)特化 / 部分特化(偏特化)

#include<iostream>
template <typename T>
struct B
{
	void fun()
	{
		std::cout << "template <template T> void B::fun()" << std::endl;
	}
};

template<>
struct B<int>
{
	void fun()
	{
		std::cout << "template<> void B<int> fun()" << std::endl;
	}
};

int main()
{
	B<int> x;
	x.fun();
	return 0;
}

在这里插入图片描述

#include<iostream>
template <typename T, typename T2>
struct B
{
	void fun()
	{
		std::cout << "template <template T, typename T2> void B::fun()" << std::endl;
	}
};

template<typename T>
struct B<int, T>
{
	void fun2()
	{
		std::cout << "template<typename T> void B<int, T> fun2()" << std::endl;
	}
};

int main()
{
	B<int, double> x;
	x.fun2();
	return 0;
}

在这里插入图片描述

template <typename T, typename T2>
struct B
{
	void fun()
	{
		std::cout << "template <template T> void B::fun()" << std::endl;
	}
};

template<typename T> //更换模板参数名称并不会对模板的代码逻辑产生影响
struct B<int, T>
{
	void fun2()
	{
		std::cout << "template<> void B<int> fun2()" << std::endl;
	}
};
#include<iostream>
template <typename T>
struct B
{
	void fun()
	{
		std::cout << "template <template T> void B::fun()" << std::endl;
	}
};

template<typename T>
struct B<T*> //部分特化
{
	void fun2()
	{
		std::cout << "template<typename T> void B<T*> fun2()" << std::endl;
	}
};

int main()
{
	B<int*> x;
	x.fun2();
	return 0;
}

在这里插入图片描述

– 特化版本与基础版本可以拥有完全不同的实现

#include<iostream>
template <typename T>
struct B
{
	void fun()
	{
		std::cout << "template <template T> void B::fun()" << std::endl;
	}
};

template<>
struct B<int>
{
	void fun2()
	{
		std::cout << "template<> void B<int> fun2()" << std::endl;
	}
};

int main()
{
	B<int> x;
	//x.fun(); //Error: class B<int>没有成员fun()
	x.fun2();
	return 0;
}

在这里插入图片描述

● 类模板的实参推导(从 C++17 开始)
– 基于构造函数的实参推导

#include<iostream>
template <typename T>
struct B
{
	B(T input)
	{}

	void fun()
	{
		std::cout << "template <template T> void B::fun()" << std::endl;
	}
};
int main()
{
	B x(3);
	return 0;
}
//CPPINSIGHTS里面的输出
#include<iostream>
template<typename T>
struct B
{
  inline B(T input)
  {
  }
  
  inline void fun()
  {
    std::operator<<(std::cout, "template <template T> void B::fun()").operator<<(std::endl);
  }
  
};

/* First instantiated from: insights.cpp:15 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
struct B<int>
{
  inline B(int input)
  {
  }
  
  inline void fun();
  
};

#endif



/* First instantiated from: insights.cpp:15 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
B(int input) -> B<int>;
#endif
int main()
{
  B<int> x = B<int>(3);
  return 0;
}

#include<iostream>
template <typename T>
struct B
{
	B(T* input)
	{}

	void fun()
	{
		std::cout << "template <template T> void B::fun()" << std::endl;
	}
};
int main()
{
  int i = 3;
  B x(&i);
	return 0;
}
//CPPINSIGHTS里面的输出
#include<iostream>
template<typename T>
struct B
{
  inline B(T * input)
  {
  }
  
  inline void fun()
  {
    std::operator<<(std::cout, "template <template T> void B::fun()").operator<<(std::endl);
  }
  
};

/* First instantiated from: insights.cpp:16 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
struct B<int>
{
  inline B(int * input)
  {
  }
  
  inline void fun();
  
};

#endif



/* First instantiated from: insights.cpp:16 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
B(int * input) -> B<int>;
#endif
int main()
{
  int i = 3;
  B<int> x = B<int>(&i);
  return 0;
}

用户自定义的推导指引
– 注意:引入实参推导并不意味着降低了类型限制!

#include<iostream>
#include<utility>
int main()
{
	std::pair<int, double> x{ 3, 3.14 };
	x.first = "123456"; //Error: 不能将 "const char *" 类型的值分配到 "int" 类型的实体
	return 0;
}

– C++ 17 之前的解决方案:引入辅助模板函数

#include<iostream>
#include<utility>
int main()
{
	std::pair x{ 3, 3.14 }; //Error: 缺少类模板 "std::pair" 的参数列表
	return 0;
}
#include<iostream>
#include<utility>
template<typename T1, typename T2>
std::pair<T1, T2> make_pair(T1 val1, T2 val2)
{
	return std::pair<T1, T2>(val1, val2);
}
int main()
{
	auto x = make_pair(3, 3.14); //OK
	return 0;
}

参考
深蓝学院:C++基础与深度解析
cppinsights
cppreference

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

C++模板基础(六) 的相关文章

  • Qt 图表和数据可视化小部件

    我已经安装了 Qt 5 7 来尝试 Qt 图表和 Qt 数据可视化 但我在 Qt Designer 和 Qt Creator 中都找不到新的小部件 有什么建议我应该做什么才能让新的小部件出现在设计器中 我今天遇到了完全相同的问题 默认情况下
  • Caliburn.Micro - ShowDialog() 如何关闭对话框?

    EDIT 新信息 刚刚设法让记录器工作 老实说 我不知道 cm 有一个 并且在尝试使用时收到此消息TryClose TryClose requires a parent IConductor or a view with a Close m
  • STL之类的容器typedef快捷方式?

    STL 容器的常见模式是这样的 map
  • 深拷贝和动态转换 unique_ptr

    假设我有一个如下所示的类 class A virtual A class B public A class C public A 我还有一个 unique ptr 向量 它是这样声明的 std vector
  • 将公历日期转换为儒略日期,然后再转换回来(随着时间)

    我正在编写一个程序 必须将当前的公历日期和时间转换为儒略日期 然后再转换回公历门 最终我需要添加能够添加年 月 日 小时 分钟和秒的功能 但我需要先解决这部分问题 现在我已经从公历日期转换为儒略日期 所以从逻辑上讲 我觉得我应该能够以某种方
  • 使用 C# 将多个音频样本混合到单个文件中

    Closed 此问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我正在寻找一个能够创建音频文件 mp3 或 wav 的库 NAudio http www codeple
  • ASP.NET - 在 RenderContent 调用中将事件处理程序添加到 Repeater 内的 LinkBut​​ton

    我有一个加载自定义用户控件的 Sharepoint WebPart 用户控件包含一个 Repeater 而 Repeater 又包含多个 LinkBut ton 在 Web 部件的 RenderContent 调用中 我有一些用于添加事件处
  • 如何从外语线程调用Python函数(C++)

    我正在开发一个程序 使用 DirectShow 来抓取音频数据 媒体文件 DirectShow 使用线程将音频数据传递给回调 我的程序中的函数 然后我让该回调函数调用另一个函数 Python 中的函数 我使用 Boost Python 来包
  • 配置:错误:无法运行C编译的程序

    我正在尝试使用 Debian Wheezy 操作系统在我的 Raspberry Pi 上安装不同的软件 当我运行尝试配置软件时 我尝试安装我得到此输出 checking for C compiler default output file
  • 更改 Xamarin.Forms 应用中顶部栏和底部栏(ControlsBar、StatusBar)的颜色

    无论如何 即使后面需要特定于平台的代码 也可以更改顶部栏 蓝色的 和底部栏 黑色的 的颜色吗 我希望添加对浅色和深色模式的支持 因此我希望能够在运行时更改它 有可能的 Android Using Window SetStatusBarCol
  • 使用数据绑定,如何将包含表情符号的文本绑定到标签并使其正确显示?

    我正在编写一个应用程序来连接 WordPress BuddyPress API 该应用程序将允许用户通过 API 相互发送消息 当这些消息包含表情符号时 我很难正确显示它们 以下是 API 返回的消息文本的简短示例 Hi x1f642 ho
  • DataGridView 行背景颜色没有改变

    我想根据加载时的特定条件更改 DGV 行的背景颜色 即使在 Windows 窗体中也是如此 但我看不到任何 DGV 行的颜色有任何变化 谁能告诉我如何解决这个问题 private void frmSecondaryPumps Load ob
  • 如何在 C# 中更改公共 IP 地址

    我正在创建一个 C winform 应用程序 我想在其中更改公共 IP 地址 而不是像 Hotspot Shield ZenMate OpenVPN 等那样更改 IPv4 地址 我已经检查了以下链接 但没有找到足够的帮助 所以我发布了这个问
  • 从 DataRow 单元格解析 int [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 如何从 DataRow 单元格解析 int 值 Int32 Parse item QuestionId ToString 这段代码可以工作 但看
  • Type.GetInterfaces() 仅适用于声明的接口

    首先 像这样的问题有很多 也许有些OP甚至在问同样的问题 问题是这些问题的答案 无论是否接受 都没有真正回答这个问题 至少我找不到 如何确定类直接声明的接口 而不是由父级或声明的接口继承的接口 e g interface I interfa
  • 如何同步nosql db(ravendb)中的更改

    我已经开始在 RavenDB 的示例上学习 NoSQL 我从一个最简单的模型开始 假设我们有由用户创建的主题 public class Topic public string Id get protected set public stri
  • 卸载程序

    我正在尝试使用此代码卸载程序 但它似乎不起作用 我尝试过其他答案 但似乎也不起作用 有人可以帮助我吗 我正在尝试按给定名称 displayName 卸载该程序 例如 我给出 displayName Appname 那么此代码应该从我的计算机
  • 当我的进程被终止时到底会发生什么?

    我有一个包含本机代码和托管代码的混合进程 在 Windows Server 2003 上运行 当我从进程资源管理器中终止进程时 它会进入 100 cpu 的状态 并在消失之前保持这种状态一段时间 有时甚至 10 分钟 在此期间我无法 杀死
  • 为什么在构造函数中设置字段是(或不是)线程安全的?

    假设您有一个像这样的简单类 class MyClass private readonly int a private int b public MyClass int a int b this a a this b b public int
  • 如何在c#中创建多线程

    我需要监听机器中的所有串行端口 假设我的机器有 4 个串行端口 我必须创建 4 个线程并开始分别使用附加线程监听每个端口 我使用此代码来获取我的机器中的端口数量 private SerialPort comPort new SerialPo

随机推荐

  • 9、Linux(Ubuntu 18)安装Redis以及C操作Redis

    扩展知识 头文件搜索 Linux中库的头文件 首先include有两种写法 一种是 include 另一种是 include xxx 这两种写法的区别是 include xxx 会首先在当前目录下搜索头文件 不递归 如果找不到的话再去系统目
  • 3分钟玩转:ES6 模块化

    ES6 模块 ES6 使用 export 和 import 导出和导入模块 导出模块 一个模块就是一个独立的 JS 文件 该文件内的变量外部无法获取 若希望能让外部获取模块内的变量 则要用 export 关键字暴露变量 分别暴露 命名行内导
  • Windows11右键菜单太烦人,简单几步即可恢复旧版完整菜单

    Windows 11已经推出一段时间了 相比Windows 10 界面确实美观了不少 同时也有很多新的设计 但是并不是每个人都能很快适应这种新设计 被广泛吐槽的一点就是右键菜单的改变 增加了显示更多选项 原来的很多右键选项被隐藏起来了 原本
  • tkinter 的界面美化库:ttkbootstrap 使用教程

    嗨害大家好鸭 我是芝士 tkbootstrap 是一个基于 tkinter 的界面美化库 使用这个工具可以开发出类似前端 bootstrap 风格的 kinter 桌面程序 如果会 tkinter 学习起来就会非常简单 如果不会的话只要先花
  • opencv python contours结构

    opencv python contours结构 经常需要构造 如果没记住内部具体结构 需要到网上处找 且找不到 就要自己findcontours然后打印出来 比较麻烦 contours的结构 比如一个box有xmin ymin xmax
  • 今天发现一个好网站 http://www.phpv.net/

    该网站的空间速度快 资料丰富 容易搜索 更新快 爽
  • 运维之道

    方法一 rc local 1 由于在centos7中 etc rc d rc local的权限被降低了 所以需要赋予其可执行权 chmod x etc rc d rc local 2 赋予脚本可执行权限 假设 opt script auto
  • pytorch训练error

    问题一 在pytorch上训练分割模型时 出现cuda runtime error 59 device side assert triggered at xxx 解决办法 通过CUDA LAUNCH BLOCKING 1 python3 m
  • python----小数点精度控制round()

    python版本也会影响结果 python2把x四舍五入为远离0的最近倍数 如round 0 5 1 round 0 5 1 python3则会把x四舍五入为最近的偶数倍数 如round 0 5 0 round 1 5 2 0 round
  • 查看解决inode使用率100%的问题

    今天登录后端服务器查看 发现程序报错日志中存在磁盘空间不足的情况 df h后发现磁盘空间充足 df ih发现 app分区inode使用率100 开始查找原因 进到 app 下 然后 for i in do echo i find i wc
  • hashMap常见的问题解答

    1 HashMap的数据结构 hashmap采取数组 链表的数据结构 在遇到哈希冲突的时候采用链表结构来解决哈希冲突 jdk1 8后分成了两种情况 bucket中元素个数大于8的时候 自动转换为红黑树的结构 目的是因为链表的查询速度比较慢
  • vue+element table 合并列

    vue element table 合并列
  • 【TCP/IP详解 卷一:协议】TCP的小结

    前言 TCP学习的综述 在学习TCP IP协议的大头 TCP协议 的过程中 遇到了很多机制和知识点 详解中更是用了足足8章的内容介绍它 TCP协议作为 应用层 和 网络层 中间的 传输层协议 既要为下面的网络层协议保证连接的可靠性 IP协议
  • 通过Jib将Springboot应用通过Docker部署

    一 安装Docker 1 更新Yum包 yum update 2 卸载旧版本 如果安装过旧版本的话 1 删除软件包 yum remove y docker docker client docker client latest docker
  • 【Espruino】NO.14 温湿度传感器DHT11

    http blog csdn net qwert1213131 article details 35828873 本文属于个人理解 能力有限 纰漏在所难免 还望指正 小鱼有点电
  • 环境变量是如何生效的——以Linux操作系统为例

    什么是环境变量 从我们学习Java开始 就经常接触一个东西 PATH 也叫环境变量 环境变量是操作系统提供给应用程序访问的简单 key value字符串 windows linux mac都有同样的概念 环境变量的作用 当我们拥有一个可执行
  • Git版本回退并强制推送到远端

    Git版本回退并强制推送到远端 本文参考廖雪峰的Git教程 前言 本文章解决问题的前提是本人不小心修改了本地代码仓库的最外层目录权限 不知道原权限是什么 导致本地git提示几十个文件被修改过 实际内容并未修改 可能是目录权限改变被git识别
  • C++ - 继承 一些 细节 - 组合 和 继承的区别

    前言 本篇博客基于 C 继承 chihiro1122的博客 CSDN博客 之上列出一些例子 如果有需要请看以上博客 继承的例子 例1 上述例子应该选择 C 首先不用说 p3肯定是指向 d 对象的开头的 p1 也是指向 d 对象的开头的 不同
  • 机器学习即服务:关于情感分析的10个应用场景和4个服务

    情感分析是什么 用户生成内容的爆炸式增长和档案材料的数字化创造了大量的数据集 其中包含了许多人对几乎每一个主题发表的观点 在某些情况下 该数据的生成是通过用户界面构造的 例如 在电子商务网站上处理客户评论相对容易 因为用户需要在产品评论的文
  • C++模板基础(六)

    类模板与成员函数模板 使用 template 关键字引入模板 template class B 类模板的声明与定义 翻译单元的一处定义原则 template