Templates and Classes___CH_19

2023-10-27

19.1 — Template classes

Templates and container classes

Template classes in the standard library

Now that we’ve covered template classes, you should understand what std::vector means now – std::vector is actually a template class, and int is the type parameter to the template! The standard library is full of predefined template classes available for your use. We’ll cover these in later chapters.

Splitting up template classes

19.2 — Template non-type parameters

Non-type parameters

#include <iostream>

template <typename T, int size> // size is an integral non-type parameter
class StaticArray
{
private:
    // The non-type parameter controls the size of the array
    T m_array[size] {};

public:
    T* getArray();

    T& operator[](int index)
    {
        return m_array[index];
    }
};

// Showing how a function for a class with a non-type parameter is defined outside of the class
template <typename T, int size>
T* StaticArray<T, size>::getArray()
{
    return m_array;
}

int main()
{
    // declare an integer array with room for 12 integers
    StaticArray<int, 12> intArray;

    // Fill it up in order, then print it backwards
    for (int count { 0 }; count < 12; ++count)
        intArray[count] = count;

    for (int count { 11 }; count >= 0; --count)
        std::cout << intArray[count] << ' ';
    std::cout << '\n';

    // declare a double buffer with room for 4 doubles
    StaticArray<double, 4> doubleArray;

    for (int count { 0 }; count < 4; ++count)
        doubleArray[count] = 4.4 + 0.1 * count;

    for (int count { 0 }; count < 4; ++count)
        std::cout << doubleArray[count] << ' ';

    return 0;
}

19.3 — Function template specialization

When instantiating a function template for a given type, the compiler stencils out a copy of the templated function and replaces the template type parameters with the actual types used in the variable declaration. This means a particular function will have the same implementation details for each instanced type (just using different types). While most of the time, this is exactly what you want, occasionally there are cases where it is useful to implement a templated function slightly different for a specific data type.

Template specialization is one way to accomplish this.

Let’s take a look at a very simple template class:

#include <iostream>

template <typename T>
class Storage
{
private:
    T m_value {};
public:
    Storage(T value)
      : m_value { value }
    {
    }

    void print()
    {
        std::cout << m_value << '\n';
    }
};

The above code will work fine for many data types:

int main()
{
    // Define some storage units
    Storage<int> nValue { 5 };
    Storage<double> dValue { 6.7 };

    // Print out some values
    nValue.print();
    dValue.print();
}

This prints:

5
6.7

Now, let’s say we want double values (and only double values) to output in scientific notation. To do so, we can use a function template specialization (sometimes called a full or explicit function template specialization) to create a specialized version of the print() function for type double. This is extremely simple: simply define the specialized function (if the function is a member function, do so outside of the class definition), replacing the template type with the specific type you wish to redefine the function for. Here is our specialized print() function for doubles:

template <>
void Storage<double>::print()
{
    std::cout << std::scientific << m_value << '\n';
}

When the compiler goes to instantiate Storage::print(), it will see we’ve already explicitly defined that function, and it will use the one we’ve defined instead of stenciling out a version from the generic templated class.

The template <> tells the compiler that this is a template function, but that there are no template parameters (since in this case, we’re explicitly specifying all of the types). Some compilers may allow you to omit this, but it’s correct to include it.

As a result, when we rerun the above program, it will print:

5
6.700000e+000

Another example

However, perhaps surprisingly, the above specialized destructor won’t compile. This is because a specialized function must specialize an explicit function (not one that the compiler is providing a default for). Since we didn’t define a destructor in Storage, the compiler is providing a default destructor for us, and thus we can’t provide a specialization. To solve this issue, we must explicitly define a destructor in Storage. Here’s the full code:

#include <iostream>
#include <string>

template <typename T>
class Storage
{
private:
    T m_value{};
public:
    Storage(T value)
        : m_value{ value }
    {
    }
    ~Storage() {}; // need an explicitly defined destructor to specialize

    void print()
    {
        std::cout << m_value << '\n';
    }
};

template <>
Storage<char*>::Storage(char* const value)
{
    if (!value)
        return;

    // Figure out how long the string in value is
    int length{ 0 };
    while (value[length] != '\0')
        ++length;
    ++length; // +1 to account for null terminator

    // Allocate memory to hold the value string
    m_value = new char[length];

    // Copy the actual value string into the m_value memory we just allocated
    for (int count = 0; count < length; ++count)
        m_value[count] = value[count];
}

template <>
Storage<char*>::~Storage()
{
    delete[] m_value;
}

int main()
{
    // Dynamically allocate a temporary string
    std::string s;

    // Ask user for their name
    std::cout << "Enter your name: ";
    std::cin >> s;

    // Store the name
    Storage<char*> storage(s.data());

    storage.print(); // Prints our name

    s.clear(); // clear the std::string

    storage.print(); // Prints our name
}

Although the above examples have all used member functions, you can also specialize non-member template functions in the same way.

19.4 — Class template specialization

template <typename T>
class Storage8
{
private:
    T m_array[8];

public:
    void set(int index, const T& value)
    {
        m_array[index] = value;
    }

    const T& get(int index) const
    {
        return m_array[index];
    }
};

Class template specialization

// Requires the Storage8 type definition from above

#include <cstdint>

template <> // the following is a template class with no templated parameters
class Storage8<bool> // we're specializing Storage8 for bool
{
// What follows is just standard class implementation details
private:
    std::uint8_t m_data{};

public:
    void set(int index, bool value)
    {
        // Figure out which bit we're setting/unsetting
        // This will put a 1 in the bit we're interested in turning on/off
        auto mask{ 1 << index };

        if (value)  // If we're setting a bit
            m_data |= mask;   // use bitwise-or to turn that bit on
        else  // if we're turning a bit off
            m_data &= ~mask;  // bitwise-and the inverse mask to turn that bit off
	}

    bool get(int index)
    {
        // Figure out which bit we're getting
        auto mask{ 1 << index };
        // bitwise-and to get the value of the bit we're interested in
        // Then implicit cast to boolean
        return (m_data & mask);
    }
};

It’s worth noting that keeping the public interface between your template class and all of the specializations similar is generally a good idea, as it makes them easier to use – however, it’s not strictly necessary.

19.5 — Partial template specialization

#include <iostream>
#include <cstring>

template <typename T, int size> // size is the expression parameter
class StaticArray
{
private:
	// The expression parameter controls the size of the array
	T m_array[size]{};

public:
	T* getArray() { return m_array; }

	T& operator[](int index)
	{
		return m_array[index];
	}
};

template <typename T, int size>
void print(StaticArray<T, size>& array)
{
	for (int count{ 0 }; count < size; ++count)
		std::cout << array[count] << ' ';
}

// overload of print() function for partially specialized StaticArray<char, size>
template <int size>
void print(StaticArray<char, size>& array)
{
	for (int count{ 0 }; count < size; ++count)
		std::cout << array[count];
}

int main()
{
	// Declare an char array of size 14
	StaticArray<char, 14> char14{};

	std::strcpy(char14.getArray(), "Hello, world!");

	// Print the array
	print(char14);

	std::cout << ' ';

	// Now declare an char array of size 12
	StaticArray<char, 12> char12{};

	std::strcpy(char12.getArray(), "Hello, mom!");

	// Print the array
	print(char12);

	return 0;
}

This prints:

Hello, world! Hello, mom!

Just as we expect.

Partial template specialization can only be used with classes, not template functions (functions must be fully specialized). Our void print(StaticArray<char, size> &array) example works because the print function is not partially specialized (it’s just an overloaded function using a class parameter that’s partially specialized).

Partial template specialization for member functions

Fortunately, there’s a workaround, by using a common base class:

#include <iostream>

template <typename T, int size> // size is the expression parameter
class StaticArray_Base
{
protected:
	// The expression parameter controls the size of the array
	T m_array[size]{};

public:
	T* getArray() { return m_array; }

	T& operator[](int index)
	{
		return m_array[index];
	}

	void print()
	{
		for (int i{ 0 }; i < size; ++i)
			std::cout << m_array[i] << ' ';
		std::cout << '\n';
	}

	virtual ~StaticArray_Base() = default;
};

template <typename T, int size> // size is the expression parameter
class StaticArray: public StaticArray_Base<T, size>
{
};

template <int size> // size is the expression parameter
class StaticArray<double, size>: public StaticArray_Base<double, size>
{
public:

	void print()
	{
		for (int i{ 0 }; i < size; ++i)
			std::cout << std::scientific << this->m_array[i] << ' ';
// note: The this-> prefix in the above line is needed.
// See https://stackoverflow.com/a/6592617 or https://isocpp.org/wiki/faq/templates#nondependent-name-lookup-members for more info on why.
		std::cout << '\n';
	}
};

int main()
{
	// declare an integer array with room for 6 integers
	StaticArray<int, 6> intArray{};

	// Fill it up in order, then print it
	for (int count{ 0 }; count < 6; ++count)
		intArray[count] = count;

	intArray.print();

	// declare a double buffer with room for 4 doubles
	StaticArray<double, 4> doubleArray{};

	for (int count{ 0 }; count < 4; ++count)
		doubleArray[count] = (4.0 + 0.1 * count);

	doubleArray.print();

	return 0;
}

This prints the same as above, but has significantly less duplicated code.

19.6 — Partial template specialization for pointers

#include <iostream>
#include <cstring>

// Our Storage class for non-pointers
template <typename T>
class Storage
{
private:
	T m_value;
public:
	Storage(T value)
        : m_value { value }
	{
	}

	~Storage()
	{
	}

	void print() const
	{
		std::cout << m_value << '\n';
	}
};

// Partial-specialization of Storage class for pointers
template <typename T>
class Storage<T*>
{
private:
	T* m_value;
public:
	Storage(T* value)
            : m_value { new T { *value } } // this copies a single value, not an array
	{
	}

	~Storage()
	{
		delete m_value;
	}

	void print() const
	{
		std::cout << *m_value << '\n';
	}
};

// Full specialization of constructor for type char*
template <>
Storage<char*>::Storage(char* value)
{
	// Figure out how long the string in value is
	int length { 0 };
	while (value[length] != '\0')
		++length;
	++length; // +1 to account for null terminator

	// Allocate memory to hold the value string
	m_value = new char[length];

	// Copy the actual value string into the m_value memory we just allocated
	for (int count = 0; count < length; ++count)
		m_value[count] = value[count];
}

// Full specialization of destructor for type char*
template<>
Storage<char*>::~Storage()
{
	delete[] m_value;
}

// Full specialization of print function for type char*
// Without this, printing a Storage<char*> would call Storage<T*>::print(), which only prints the first char
template<>
void Storage<char*>::print() const
{
	std::cout << m_value;
}

int main()
{
	// Declare a non-pointer Storage to show it works
	Storage<int> myint { 5 };
	myint.print();

	// Declare a pointer Storage to show it works
	int x { 7 };
	Storage<int*> myintptr { &x };

	// If myintptr did a pointer assignment on x,
	// then changing x will change myintptr too
	x = 9;
	myintptr.print();

	// Dynamically allocate a temporary string
	char* name { new char[40]{ "Alex" } };

	// Store the name
	Storage<char*> myname { name };

	// Delete the temporary string
	delete[] name;

	// Print out our name to prove we made a copy
	myname.print();
}

This works as we expect:

5
7
Alex

Using partial template class specialization to create separate pointer and non-pointer implementations of a class is extremely useful when you want a class to handle both differently, but in a way that’s completely transparent to the end-user.

19.x — Chapter 19 comprehensive quiz

Template specialization can be used when we want to override the default behavior from the templated function or class for a specific type. If all types are overridden, this is called full specialization. Classes also support partial specialization, where only some of the templated parameters are specialized. Functions can not be partially specialized.

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

Templates and Classes___CH_19 的相关文章

  • .NET 单点登录

    我一直在尝试使用 C 为 NET Web 应用程序实现 WEB SSO 服务提供程序插件 我将使用 shibboleth 身份提供商 我已经使用 OpenSAML 库为 java 应用程序实现了相同的功能 我想知道在 NET 应用程序中使用
  • .crt 部分?这个警告是什么意思?

    我最近收到此警告 VC 2010 warning LNK4210 CRT section exists there may be unhandled static initializers or terminators 我假设这是关键部分
  • 自动映射器多对多 stackoverflowException

    我遇到以下映射的堆栈溢出 Mapper CreateMap
  • 深拷贝和动态转换 unique_ptr

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

    我正在编写一个程序 必须将当前的公历日期和时间转换为儒略日期 然后再转换回公历门 最终我需要添加能够添加年 月 日 小时 分钟和秒的功能 但我需要先解决这部分问题 现在我已经从公历日期转换为儒略日期 所以从逻辑上讲 我觉得我应该能够以某种方
  • 将 dataGridView 中选定的行作为对象检索

    我有一堂这样的课 public partial class AdressBokPerson public long Session get set public string F rnamn get set public string Ef
  • 控制台应用程序中使用 Unicode 字符的 _tprintf

    我正在从 Unicode 构建的控制台应用程序 使用 C 和 Visual Studio 2008 执行这个简单的输出 此代码旨在在 Windows 上运行 tprintf L Some sample string n 一切正常 但是如果我
  • C# 枚举到字符串自动转换?

    是否可以让编译器自动将我的 Enum 值转换为字符串 这样我就可以避免每次都显式调用 ToString 方法 这是我想做的一个例子 enum Rank A B C Rank myRank Rank A string myString Ran
  • 如何将STL容器数据转储到gdb中?

    我无法在 gdb 中转储 STL 无序映射容器值 变量类型是 std unordered map var 我的 gdb 版本 7 7 1 GDB配置 configure host x86 64 linux gnu target x86 64
  • 应用程序处于中断模式。您的应用程序已进入中断状态,

    我发现自己遇到了同样的问题here https stackoverflow com questions 36204009 disable break mode page in vs2015 我在 dll 中使用 Windows 窗体 这是针
  • 配置:错误:无法运行C编译的程序

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

    无论如何 即使后面需要特定于平台的代码 也可以更改顶部栏 蓝色的 和底部栏 黑色的 的颜色吗 我希望添加对浅色和深色模式的支持 因此我希望能够在运行时更改它 有可能的 Android Using Window SetStatusBarCol
  • 在 C# 中使用命名空间别名有什么好处? [复制]

    这个问题在这里已经有答案了 使用命名空间别名有什么好处 仅仅是为了简化编码吗 仅当与类发生冲突时我才使用名称空间别名 对我来说 这根本没有简化 我的意见是 如果没有必要 就不要使用
  • Gremlin.net 文本包含等效项

    我正在使用 Gremlin net 库连接到 janus 图形服务器 我使用 cassandra 和弹性搜索进行数据存储和索引 在我使用的 gremlin 语言和 gremlin 控制台中文本包含在属性的文本中进行搜索 我正在使用混合索引
  • 如何检测应用程序正在运行的 .NET 版本?

    我尝试使用Environment Version ToString 确定目标计算机上正在使用什么 NET 框架 但安装了 4 0 版本时 它说我正在使用 NET 2 0 如何检测目标计算机上正在运行的 NET Framework 版本 En
  • C 变量声明的效率 [重复]

    这个问题在这里已经有答案了 例如 在 C 中声明一个变量需要多长时间int x or unsigned long long var 我想知道它是否会让我的代码在类似的事情中更快 for conditions int var 0 code 这
  • 使用多态对象数组进行 JSON 反序列化

    我在涉及多态对象数组的 JSON 反序列化方面遇到问题 我已经尝试过记录的序列化解决方案here https stackoverflow com questions 5186973 json serialization of array w
  • 卸载程序

    我正在尝试使用此代码卸载程序 但它似乎不起作用 我尝试过其他答案 但似乎也不起作用 有人可以帮助我吗 我正在尝试按给定名称 displayName 卸载该程序 例如 我给出 displayName Appname 那么此代码应该从我的计算机
  • 使用 CodeDOM 将程序集添加到 BuildManager 会导致间歇性错误

    我正在使用 CodeDOM 在运行时创建内存中程序集 如下所示 public Assembly Compile CodeCompileUnit targetUnit string path Path GetDirectoryName new
  • 如何使用 C# 为 azure devops 变量赋值

    我有 selenium C 测试脚本 可以从浏览器获取令牌 我有两个 azure devops 任务 一个用于执行 selenium 测试 另一个用于执行 API 测试 我想将 selenium 测试获取的令牌传递给 API 测试执行任务

随机推荐

  • javascript 防抖节流

    Javascript防抖和节流的深入理解和实践 http www ptbird cn javascript anti shake throttle html https www cnblogs com momo798 p 9177767 h
  • @RequestMapping用法详解

    这个相当于servlet的请求配置
  • [LeetCode-88]-Merge Sorted Array(有序数组合并)

    文章目录 0 题目相关 1 Solution 2 其他改进方案 0 题目相关 题目解读 给定两个有序数组 对数组进行合并操作 要求合并后的数组依旧有序 原题描述 原题链接 Given two sorted integer arrays nu
  • 【第39篇】RepLKNet将内核扩展到 31x31:重新审视 CNN 中的大型内核设计

    文章目录 摘要 一 简介 二 相关工作 2 1 大内核模型 2 2 模型缩放技术 2 3 结构重新参数化 三 应用大卷积的指南 四 RepLKNet 大内核架构 4 1 架构规范 4 2 使大内核更大 4 3 ImageNet 分类 4 5
  • python,pycharm 的环境变量设置

    官网下载安装python解释器时 如果忘记勾选添加到环境变量 add to path 可进行如下操作 来添加 更改环境变量 1 右键单击桌面上的 此计算机 图标 然后选择 属性 2 打开系统窗口并单击左侧的 高级系统设置 3 单击系统属性底
  • 如何让网页自适应所有屏幕宽度

    随着网络的快熟发展 越来越多的人使用手机上网 移动设备正超过桌面设备 成为访问互联网的最常见终端 于是 网页设计师不得不面对一个难题 如何才能在不同大小的设备上呈现同样的网页 手机的屏幕比较小 宽度通常在600像素以下 PC的屏幕宽度 一般
  • Markdown 文本居中、字体颜色以及数学公式

    文章目录 文本格式 文字居中 字体 字体颜色 字体大小 背景色 颜色 数学公式 希腊字母 行内与独行 上标 下标与组合 汉字 字体与格式 占位符 定界符与组合 四则运算 高级运算 逻辑运算 集合运算 数学符号 文本格式 文字居中 1 文字居
  • 数据结构---求最大公约数

    求最大公约数 穷举法 辗转相除法法 第一步 第二步 第三步 JAVA实现 更相减损术 第一步 第二步 第三步 JAVA实现 更相减损术与移位相结合 操作逻辑 例子 JAVA实现 写一段代码 求出两个整数的最大公约数 要尽量优化算法的性能 例
  • Python 处理错误和异常

    微信公众号 数据分析与统计学习如有问题或建议 请公众号留言最近更新时间 2018 7 2 一 前言 Python的系列文章主要介绍python语言的基础语法知识 按照核心内建数据类型 语句 函数 类 异常 标准模块的顺序对相关的语法知识进行
  • imx6ull开发板,usb免驱摄像头的配置

    在 dev下面 只能找到video0 说明开发板并没有识别出有新连接进来的摄像头 需要在内核中 配置支持UVC标准的USB驱动 重新配置即可
  • cve 爬虫_爬虫CNVD构建漏洞库

    import requests from lxml import etree import xlsxwriter from requests utils import add dict to cookiejar import execjs
  • 关于电路交换、报文交换和分组交换

    交换 所谓交换 指的就是服务器与服务器之间的数据交换 考纲要求掌握三种 电路交换 报文交换和分组交换 这篇文章主要介绍这三种交换方式 以及分组交换中的数据报和虚电路 电路交换 电路交换即在通信之前 在通信双方之间建立一条被双方独占的物理通路
  • obsidian加git备份,同时忽略掉自己不想同步的文件夹

    最近想用这个语雀进行知识库的分享 但是这个语雀的会员费太贵了 思来想去还是用 git 比较好 因为这个知识库的内容都是自己的笔记 为了能够访问的更加方便我选择了这个 gitte 而不是 github 我的知识库链接 knowledge 第一
  • 如何在sqlplus执行sql文件

    1 Oracle数据库的sqlplus可以直接执行SQL语句吗 2 SQL Plus中怎么执行多个 sql脚本文件 3 如何在sqlplus执行sql文件 4 怎样在sqlplus中批量执行sql文件 Oracle数据库的sqlplus可以
  • 在模仿中精进数据分析与可视化01——颗粒物浓度时空变化趋势(Mann–Kendall Test)

    本文是在模仿中精进数据分析与可视化系列的第一期 颗粒物浓度时空变化趋势 Mann Kendall Test 主要目的是参考其他作品模仿学习进而提高数据分析与可视化的能力 如果有问题和建议 欢迎在评论区指出 若有其他想要看的作品 也欢迎在评论
  • GIT合并分支的三种方法

    一 使用merge命令合并分支 1 目标 将dev分支合并到master分支 1 1 首先切换到master分支上 git checkout master 1 2 如果是多人开发的话 需要把远程master上的代码pull下来 git pu
  • 【华为OD机试 】 单词搜索(C++ Java JavaScript Python)

    题目描述 找到它是一个小游戏 你需要在一个矩阵中找到给定的单词 假设给定单词 HELLOWORD 在矩阵中只要能找到 H gt E gt L gt L gt O gt W gt O gt R gt L gt D连成的单词 就算通过 注意区分
  • flask 写数据mysql_Flask从入门到精通之MySQL数据库操作

    前面的章节中我们已经学习了如何建立模型和关系 接下来我们学习如何使用模型的最好方法是在Python shell 中实际操作 并将介绍最常用的数据库操作 一 创建表 首先 我们要让Flask SQLAlchemy 根据模型类创建数据库 方法是
  • 27.EI文章复现《高比例清洁能源接入下计及需求响应的配电网重构》

    下载地址 高比例清洁能源接入下计及需求响应的配电网重构 1主要内容 该程序复现 高比例清洁能源接入下计及需求响应的配电网重构 以考虑网损成本 弃风弃光成本和开关操作惩罚成本的综合成本最小为目标 针对配电网重构模型的非凸性 引入中间变量并对其
  • Templates and Classes___CH_19

    19 1 Template classes Templates and container classes Template classes in the standard library Now that we ve covered te