模板
C++ 的模板一直是这门语言的一种特殊的艺术,模板甚至可以独立作为一门新的语言来进行使用。模板的哲学在于将一切能够在编译期处理的问题丢到编译期进行处理,仅在运行时处理那些最核心的动态服务,进而大幅优化运行期的性能。因此模板也被很多人视作 C++ 的黑魔法之
外部模板
传统 C++ 中,模板只有在使用时才会被编译器实例化。换句话说,只要在每个编译单元(文件)中编译的代码中遇到了被完整定义的模板,都会实例化这就产生了重复实例化而导致的编译时间的增加。并且,我们没有办法通知编译器不要触发模板的实例化
template class std::vector; 强行实例化
extern template class std::vector; 不在该当前编译文件中实例化模板
尖括号
在传统 C++ 的编译器中,>> 一律被当做右移运算符来进行处理。但实际上我们很容易就写出了嵌套模板的代码
template<bool T>
class MagicType {
bool magic = T;
};
// in main function:
std::vector<MagicType<(1>2)>> magic; // 合法, 但不建议写出这样的代码
类别名
在了解类型别名模板之前,需要理解『模板』和『类型』之间的不同。仔细体会这句话:模板是用来产生类型的。在传统 C++ 中,typedef 可以为类型定义一个新的名称,但是却没有办法为模板定义一个新的名称。因为,模板不是类型。例如:
template<typename T,typename U>
class MagicType
{
public:
T dark;
U magic;
}
//不合法
template<typename T>
typedef MagicType<std::vector<T>, std::string> FakeDarkMagic;
C++11 使用 using 引入了下面这种形式的写法,并且同时支持对传统 typedef 相同的功效:通常我们使用 typedef 定义别名的语法是:typedef 原名称 新名称;,但是对函数指针等别名的定义语法却不相同,这通常给直接阅读造成了一定程度的困难
typedef int (*process)(void *);
using NewProcess = int(*)(void *);
template<typename T>
using TrueDarkMagic = MagicType<std::vector<T>, std::string>;
int main() {
TrueDarkMagic<bool> you;
}
默认模板参数
template<typename T = int, typename U = int>
auto add(T x, U y) -> decltype(x+y) {
return x+y;
}
变长参数模板
模板一直是 C++ 所独有的黑魔法(一起念:Dark Magic)之一。在 C++11 之前,无论是类模板还是函数模板,都只能按其指定的样子,接受一组固定数量的模板参数;而 C++11 加入了新的表示方法,允许任意个数、任意类别的模板参数,同时也不需要在定义时将参数的个数固定
template<typename... Ts> class Magic;
变长参数模板也能被直接调整到到模板函数上。传统 C 中的 printf 函数,虽然也能达成不定个数的形参的调用,但其并非类别安全。而 C++11 除了能定义类别安全的变长参数函数外,还可以使类似printf 的函数能自然地处理非自带类别的对象。除了在模板参数中能使用 … 表示不定长模板参数外,函数参数也使用同样的表示法代表不定长参数,这也就为我们简单编写变长参数函数提供了便捷的手段,
例如:
template<typename... Args>
void printf(const std::string &str, Args... args);
解包
template<typename... Ts>
void magic(Ts... args)
{
std::cout<<sizeof...(args)<<std::endl;
}
//结果
magic(); // 输出 0
magic(1); // 输出 1
magic(1, ""); // 输出 2
递归模板参数
递归是非常容易想到的一种手段,也是最经典的处理方法。这种方法不断递归地向函数传递模板参数,进而达到递归遍历所有模板参数的目的
#include<iostream>
template<typename TO>
void printf(TO value)
{
std::cout<<value<<std::endl;
}
template<typename T, typename... Ts>
void printf1(T value, Ts... args) {
std::cout << value << std::endl;
printf1(args...);
}
int main() {
printf1(1, 2, "123", 1.1);
return 0; }
你应该感受到了这很繁琐,在 C++17 中增加了变参模板展开的支持,于是你可以在一个函数中完成 printf 的编写:
template<typename T0, typename... T>
void printf2(T0 t0, T... t) {
std::cout << t0 << std::endl;
if constexpr (sizeof...(t) > 0) printf2(t...);
}
递归模板函数是一种标准的做法,但缺点显而易见的在于必须定义一个终止递归的函数。
template<typename T, typename... Ts>
auto printf3(T value, Ts... args) {
std::cout << value << std::endl;
(void) std::initializer_list<T>
{
([&args] {std::cout << args << std::endl;}(), value)...};
}
通过初始化列表,(lambda 表达式, value)… 将会被展开。由于逗号表达式的出现,首先会执行前面的 lambda 表达式,完成参数的输出。为了避免编译器警告,我们可以将 std::initializer_list显式的转为 void。
折叠表达式
#include <iostream>
template<typename ... T>
auto sum(T ... t) {
return (t + ...);
}
int main() {
std::cout << sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) << std::endl;
}
template<typename T,typename U>
auto add(T t,U u)
{
return t+u;
}
template<typename T,int BufSize>
class buffer_t
{
public:
T& alloc();
void free(T& item);
private:
T data[Bufsize];
}
buffer_t<int,100> buf;//100作为模板参数
template <auto value> void foo() {
std::cout << value << std::endl;
return; }
int main() {
foo<10>(); // value 被推导为 int 类型
}
面向对象
委托构造
,从而达到简化代码的目的:
#include<iostream>
class Base{
public:
int value1;
int value2;
Base()
{
value1=1;
}
Base(int value):Base()//委托构造
{
value2=value;
}
};
int main()
{
Base b(2);
std::cout << b.value1 << std::endl;
std::cout << b.value2 << std::endl;
}
继承构造
#include <iostream>
class Base {
public:
int value1;
int value2;
Base() {
value1 = 1; }
Base(int value) : Base() { // 委托 Base() 构造函数
value2 = value;
}
};
class Subclass : public Base {
public:
using Base::Base; // 继承构造
};
int main() {
Subclass s(3);
std::cout << s.value1 << std::endl;
std::cout << s.value2 << std::endl;
}
struct Base {
virtual void foo(int);
};
struct SubClass: Base {
virtual void foo(int) override; // 合法
virtual void foo(float) override; // 非法, 父类没有此虚函数
};
struct Base {
virtual void foo() final;
};
struct SubClass1 final: Base {
}; // 合法
struct SubClass2 : SubClass1 {
}; // 非法, SubClass1 已 final
struct SubClass3: Base {
void foo(); // 非法, foo 已 final
};
显式禁用默认函数
原文地址:https://blog.csdn.net/qq_62309585/article/details/126710741
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.7code.cn/show_23674.html
如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除!