模板
概述
- C++模板是一个强大的编程工具,使得可以编写通用的、类型安全的代码
- 模板主要用于函数和类的泛型编程,允许你定义通用算法和数据结构,然后在需要时使用具体类型进行实例化
使用场景
- 通用算法:
- 模板允许你编写通用算法,如排序、搜索等,而无需针对每种类型重复实现
- 容器类:
- 标准模板库(
STL)中的容器类(如std::vector、std::list等)都是使用模板实现的,能够存储不同类型的数据
- 标准模板库(
- 智能指针:
- 如
std::unique_ptr和std::shared_ptr,使用模板实现通用的内存管理
- 如
函数模板
概述
- 函数模板允许你编写一次函数定义,然后使用不同的类型实例化该函数
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#include <iostream> // 函数模板 template<typename T> T add(T a, T b) { return a + b; } int main() { int intResult = add(1, 2); // 使用int实例化 double doubleResult = add(1.1, 2.2); // 使用double实例化 std::cout << "intResult: " << intResult << std::endl; std::cout << "doubleResult: " << doubleResult << std::endl; return 0; } |
类模板
概述
- 类模板允许你定义通用的数据结构,然后用不同的类型实例化
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
#include <iostream> // 类模板 template<typename T> class Pair { public: Pair(T first, T second) : first_(first), second_(second) {} T getFirst() const { return first_; } T getSecond() const { return second_; } private: T first_; T second_; }; int main() { Pair<int> intPair(1, 2); // 使用int实例化 Pair<std::string> stringPair("Hello", "World"); // 使用std::string实例化 std::cout << "intPair: " << intPair.getFirst() << ", " << intPair.getSecond() << std::endl; std::cout << "stringPair: " << stringPair.getFirst() << ", " << stringPair.getSecond() << std::endl; return 0; } |
模板参数
概述
- 模板还可以接受非类型参数,这些参数在编译时是常量
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
#include <iostream> // 非类型模板参数 template<typename T, int Size> class Array { public: T& operator[](int index) { return data_[index]; } int size() const { return Size; } private: T data_[Size]; }; int main() { Array<int, 5> intArray; for (int i = 0; i < intArray.size(); ++i) { intArray[i] = i * 10; } for (int i = 0; i < intArray.size(); ++i) { std::cout << intArray[i] << " "; } std::cout << std::endl; return 0; } |
非类型模板参数
概述
- 非类型模板参数(
Non-Type Template Parameters,NTTP)是在模板中使用的常量值,而不是类型- 这些参数可以是整数、指针、引用、枚举或其他常量表达式
- 非类型模板参数允许你在编译时指定一些固定的值,从而使模板实例化时可以根据这些值生成不同的代码
非类型模板参数的常见类型
- 整数:
- 常见的整数类型,如
int、char、bool等
- 常见的整数类型,如
- 指针:
- 指向对象或函数的指针
- 引用:
- 对对象的引用
- 枚举:
- 枚举常量
非类型模板参数的限制
- 必须是常量表达式:
- 非类型模板参数必须是编译时可以确定的常量表达式
- 类型限制:
- 只能是整数类型、指针类型、引用类型、枚举类型或常量表达式
应用场景
- 固定大小的数组或容器:
- 如下面的数组模板示例
- 静态配置:
- 在编译时确定某些配置参数,从而生成特定的代码路径
- 编译时多态:
- 根据非类型模板参数生成不同的代码实现,实现编译时的多态性
高级用法
- 非类型模板参数可以与模板元编程相结合,实现编译时的常量计算
- 见模板元编程示例
示例
- 整数非类型模板参数
- 这个示例中,
Array模板使用了一个int类型的非类型模板参数Size来指定数组的大小
- 这个示例中,
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
#include <iostream> // 数组类模板,使用非类型模板参数来指定数组大小 template<typename T, int Size> class Array { public: T& operator[](int index) { return data_[index]; } int size() const { return Size; } private: T data_[Size]; }; int main() { Array<int, 5> intArray; // 实例化一个大小为5的整数数组 for (int i = 0; i < intArray.size(); ++i) { intArray[i] = i * 10; } for (int i = 0; i < intArray.size(); ++i) { std::cout << intArray[i] << " "; } std::cout << std::endl; return 0; } |
- 指针非类型模板参数
- 这个示例中,
callFunction模板使用了一个指向void()类型函数的指针作为非类型模板参数
- 这个示例中,
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#include <iostream> // 函数模板,使用非类型模板参数来指定函数指针 template<void (*Func)()> void callFunction() { Func(); } void hello() { std::cout << "Hello, World!" << std::endl; } int main() { callFunction<hello>(); // 实例化模板并调用函数 return 0; } |
- 引用非类型模板参数
- 这个示例中,
ReferenceWrapper模板使用了一个int类型的引用作为非类型模板参数
- 这个示例中,
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#include <iostream> // 类模板,使用非类型模板参数来指定引用 template<int& Ref> class ReferenceWrapper { public: void print() const { std::cout << Ref << std::endl; } }; int main() { int value = 42; ReferenceWrapper<value> refWrapper; // 实例化模板并引用变量 refWrapper.print(); return 0; } |
模板特化
概述
-
在C++模板编程中,特化是指为某些特定类型或条件提供不同于通用模板的实现
- 模板特化允许你为特定类型提供特殊实现
-
特化分为两种:
- 全特化(全模板特化)
- 偏特化(部分特化)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
#include <iostream> // 通用模板 template<typename T> class TypePrinter { public: static void print() { std::cout << "Unknown type" << std::endl; } }; // 模板特化 template<> class TypePrinter<int> { public: static void print() { std::cout << "Type is int" << std::endl; } }; template<> class TypePrinter<double> { public: static void print() { std::cout << "Type is double" << std::endl; } }; int main() { TypePrinter<int>::print(); // 输出: Type is int TypePrinter<double>::print(); // 输出: Type is double TypePrinter<char>::print(); // 输出: Unknown type return 0; } |
全特化(全模板特化)
- 全特化(
Explicit Specialization)是为某个特定类型提供完全不同的实现 - 全特化通常用于函数模板和类模板
- 函数模板的全特化示例:
- 这个例子中,
add函数模板有一个全特化版本,用于处理const char*类型的参数
- 这个例子中,
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#include <iostream> // 通用模板 template<typename T> T add(T a, T b) { return a + b; } // 全特化版本,用于处理const char* template<> const char* add(const char* a, const char* b) { static std::string result; result = std::string(a) + std::string(b); return result.c_str(); } int main() { std::cout << add(1, 2) << std::endl; // 使用通用模板 std::cout << add(1.5, 2.5) << std::endl; // 使用通用模板 std::cout << add("Hello, ", "World!") << std::endl; // 使用全特化模板 return 0; } |
- 类模板的全特化示例:
- 这个例子中,
TypePrinter类模板有两个全特化版本,分别用于处理int和double类型
- 这个例子中,
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
#include <iostream> // 通用类模板 template<typename T> class TypePrinter { public: static void print() { std::cout << "Unknown type" << std::endl; } }; // 全特化版本,用于处理int template<> class TypePrinter<int> { public: static void print() { std::cout << "Type is int" << std::endl; } }; // 全特化版本,用于处理double template<> class TypePrinter<double> { public: static void print() { std::cout << "Type is double" << std::endl; } }; int main() { TypePrinter<int>::print(); // 输出: Type is int TypePrinter<double>::print(); // 输出: Type is double TypePrinter<char>::print(); // 输出: Unknown type return 0; } |
偏特化(部分特化)
- 偏特化(
Partial Specialization)是为某些特定的模板参数模式提供不同的实现 - 偏特化通常用于类模板,允许为部分类型参数提供特化版本
- 类模板的偏特化示例:
- 这个例子中,
Pair类模板有一个部分特化版本,当第二个模板参数是int时,使用特化版本
- 这个例子中,
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
#include <iostream> // 通用模板 template<typename T1, typename T2> class Pair { public: Pair(T1 first, T2 second) : first_(first), second_(second) {} T1 getFirst() const { return first_; } T2 getSecond() const { return second_; } private: T1 first_; T2 second_; }; // 部分特化版本,当第二个参数是int时 template<typename T> class Pair<T, int> { public: Pair(T first, int second) : first_(first), second_(second) {} T getFirst() const { return first_; } int getSecond() const { return second_; } void print() const { std::cout << "Pair with int: " << first_ << ", " << second_ << std::endl; } private: T first_; int second_; }; int main() { Pair<std::string, int> stringIntPair("Age", 30); // 使用偏特化版本 stringIntPair.print(); // 输出: Pair with int: Age, 30 Pair<int, double> intDoublePair(1, 2.5); // 使用通用模板 std::cout << "Pair: " << intDoublePair.getFirst() << ", " << intDoublePair.getSecond() << std::endl; return 0; } |
对比全特化与偏特化
- 全特化:
- 为特定类型提供完全不同的实现。
- 使用场景:
当需要为某个特定类型提供特殊处理逻辑时 - 适用于函数模板和类模板
- 偏特化:
- 为某些特定的模板参数模式提供不同的实现。
- 使用场景:
当需要为一组类型模式提供特殊处理逻辑时,特别是当模板参数较多,且只有部分参数需要特化时 - 主要用于类模板
模板和友元
概述
- 友元(
friend)在模板编程中可以用来访问类的私有成员和保护成员- 友元声明可以用于函数、类和模板,使得它们能够访问指定类的非公有成员
友元在模板中的应用
- 函数模板作为类模板的友元
- 在类模板中,可以声明一个函数模板作为其友元,使得该函数模板可以访问类模板的私有成员
- 示例:
这个例子中,print函数模板被声明为MyClass类模板的友元,从而能够访问MyClass的私有成员value_
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
#include <iostream> template<typename T> class MyClass { // 声明函数模板为友元 template<typename U> friend void print(const MyClass<U>& obj); public: MyClass(T value) : value_(value) {} private: T value_; }; // 函数模板定义 template<typename U> void print(const MyClass<U>& obj) { std::cout << "Value: " << obj.value_ << std::endl; } int main() { MyClass<int> intObj(42); MyClass<double> doubleObj(3.14); print(intObj); // 输出: Value: 42 print(doubleObj); // 输出: Value: 3.14 return 0; } |
- 类模板作为类模板的友元:
- 可以声明一个类模板作为另一个类模板的友元,使得友元类模板可以访问该类模板的私有成员
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
#include <iostream> template<typename T> class MyClass; // 类模板声明为友元 template<typename T> class FriendClass { public: void show(const MyClass<T>& obj); }; template<typename T> class MyClass { // 声明 FriendClass 为友元 friend class FriendClass<T>; public: MyClass(T value) : value_(value) {} private: T value_; }; template<typename T> void FriendClass<T>::show(const MyClass<T>& obj) { std::cout << "Value: " << obj.value_ << std::endl; } int main() { MyClass<int> intObj(42); FriendClass<int> friendObj; friendObj.show(intObj); // 输出: Value: 42 return 0; } |
- 具体类型的友元:
- 你还可以为类模板的具体实例化声明友元
- 这在需要为特定类型提供特殊访问权限时非常有用
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
#include <iostream> template<typename T> class MyClass { // 具体类型的友元声明 friend void printInt(const MyClass<int>& obj); public: MyClass(T value) : value_(value) {} private: T value_; }; // 具体类型的友元函数定义 void printInt(const MyClass<int>& obj) { std::cout << "Value (int): " << obj.value_ << std::endl; } int main() { MyClass<int> intObj(42); MyClass<double> doubleObj(3.14); printInt(intObj); // 输出: Value (int): 42 // printInt(doubleObj); // 编译错误,double 类型的实例不是友元 return 0; } |
注意
- 访问控制:
- 友元关系可以破坏类的封装性,应谨慎使用,仅在必要时使用
- 代码维护:
- 友元关系增加了类之间的耦合度,可能影响代码的可维护性和可读性
- 模板编程:
- 在模板编程中使用友元需要注意友元声明的位置和模板参数的正确匹配
总结
- 用于定义能够访问类模板私有成员的函数或类
模板元编程
概述
- 使用模板进行编译时计算,如计算常量表达式或生成类型列表
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
#include <iostream> // 阶乘的模板元编程实现 template<int N> struct Factorial { static const int value = N * Factorial<N - 1>::value; }; // 基础情况 template<> struct Factorial<0> { static const int value = 1; }; int main() { std::cout << "Factorial of 5: " << Factorial<5>::value << std::endl; // 输出: 120 return 0; } |
原理
- 所谓编译时计算,是不是就是利用这种编程方法,在编译期完成对一些东西的计算,编译期生成汇编指令的时候,把某些地方的调用直接替换成编译期的计算结果?
- 所谓编译时计算(
compile-time computation)是指在编译阶段而不是运行阶段进行计算 - 模板元编程(
Template Metaprogramming)利用C++模板的特性,在编译期执行一些计算,并将结果用于生成代码 - 这种方法在编译阶段就完成了计算,避免了运行时的开销
- 所谓编译时计算(
- 编译时计算的核心在于编译器在编译代码时解析模板,进行必要的计算,并将计算结果直接嵌入生成的目标代码中
- 这意味着在编译生成汇编指令时,编译器已经知道并可以利用计算结果,从而优化代码
模板元编程的应用
- 编译时常量计算:
- 如阶乘、斐波那契数列等
- 类型属性查询:
- 如
std::is_integral、std::is_pointer等类型特性查询 - 这个示例中,
std::is_pointer是一个编译时常量表达式,可以在编译时确定类型属性,从而在编译期生成不同的代码
- 如
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
#include <iostream> #include <type_traits> // 判断类型是否为指针类型 template<typename T> void checkPointerType() { if (std::is_pointer<T>::value) { std::cout << "T is a pointer type." << std::endl; } else { std::cout << "T is not a pointer type." << std::endl; } } int main() { checkPointerType<int>(); // 输出: T is not a pointer type. checkPointerType<int*>(); // 输出: T is a pointer type. return 0; } |
- 静态断言:
- 利用
static_assert在编译期进行条件检查
静态断言可以用于在编译期检查条件,如果条件不满足,编译器会生成错误
- 利用
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#include <iostream> // 编译时检查 template<int N> struct CompileTimeCheck { static_assert(N > 0, "N must be greater than 0"); static const int value = N; }; int main() { std::cout << "Value: " << CompileTimeCheck<5>::value << std::endl; // 输出: Value: 5 // std::cout << "Value: " << CompileTimeCheck<-1>::value << std::endl; // 编译错误: N must be greater than 0 return 0; } |
- 元函数:
- 如
std::conditional、std::enable_if等元函数用于模板条件编译和SFINAE(Substitution Failure Is Not An Error)
- 如
SFINAE
概述
SFINAE(Substitution Failure Is Not An Error):一种模板编程技巧,用于实现条件编译- 它允许编译器在模板参数替换失败时不产生编译错误,而是尝试其他重载或特化
- 这一特性使得模板元编程变得非常强大和灵活,尤其是在编写类型安全的库和泛型算法时
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
#include <iostream> #include <type_traits> // 泛型函数,支持不同类型 template<typename T> typename std::enable_if<std::is_integral<T>::value, void>::type print(T value) { std::cout << "Integral value: " << value << std::endl; } template<typename T> typename std::enable_if<std::is_floating_point<T>::value, void>::type print(T value) { std::cout << "Floating point value: " << value << std::endl; } int main() { print(10); // 输出: Integral value: 10 print(3.14); // 输出: Floating point value: 3.14 return 0; } |
原理
- 当编译器在实例化模板时,如果某些模板参数导致了无效的模板定义(如无效类型或无效表达式),编译器不会立即报错,而是会忽略这个特定的模板实例化,并继续查找其他匹配的模板
- 这种机制允许编译器根据不同的类型或条件选择合适的模板实例化
用途
- 函数重载:
- 根据模板参数的类型或属性选择不同的函数重载
- 模板特化:
- 根据模板参数的类型或属性选择不同的模板特化
- 类型检查:
- 在编译期进行类型检查和条件编译
实现方式
- 通过函数重载实现SFINAE
- 上面概述的示例代码
- 通过类模板特化实现SFINAE
std::enable_if用于在编译期选择合适的模板特化
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
#include <iostream> #include <type_traits> // 通用模板 template<typename T, typename Enable = void> class TypeTraits { public: static void printType() { std::cout << "Unknown type" << std::endl; } }; // 部分特化模板,适用于指针类型 template<typename T> class TypeTraits<T, typename std::enable_if<std::is_pointer<T>::value>::type> { public: static void printType() { std::cout << "Pointer type" << std::endl; } }; // 部分特化模板,适用于非指针类型 template<typename T> class TypeTraits<T, typename std::enable_if<!std::is_pointer<T>::value>::type> { public: static void printType() { std::cout << "Non-pointer type" << std::endl; } }; int main() { TypeTraits<int>::printType(); // 输出: Non-pointer type TypeTraits<int*>::printType(); // 输出: Pointer type TypeTraits<double>::printType(); // 输出: Non-pointer type return 0; } |
高级用法
- 多重
SFINAE条件
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#include <iostream> #include <type_traits> // 适用于整数类型和双精度浮点类型 template<typename T> typename std::enable_if<std::is_integral<T>::value || std::is_same<T, double>::value, void>::type printType(T value) { std::cout << "Integral or double type: " << value << std::endl; } // 适用于其他类型 template<typename T> typename std::enable_if<!std::is_integral<T>::value && !std::is_same<T, double>::value, void>::type printType(T value) { std::cout << "Other type: " << value << std::endl; } int main() { printType(42); // 输出: Integral or double type: 42 printType(3.14); // 输出: Other type: 3.14 printType(3.14f); // 输出: Other type: 3.14 printType("Hello"); // 输出: Other type: Hello return 0; } |
std::void_tstd::void_t是一个在C++17中引入的工具,简化了SFINAE的实现- 它用于检测表达式的有效性
- 示例:
这个示例中,HasTypeMember用于检测一个类是否有名为Type的类型成员。std::void_t简化了这一检测过程
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
#include <iostream> #include <type_traits> template<typename, typename = std::void_t<>> struct HasTypeMember : std::false_type {}; // 如果T有一个名为Type的类型成员,特化为true template<typename T> struct HasTypeMember<T, std::void_t<typename T::Type>> : std::true_type {}; // 示例类,有Type成员 class WithType { public: using Type = int; }; // 示例类,没有Type成员 class WithoutType {}; int main() { std::cout << std::boolalpha; std::cout << "WithType has Type member: " << HasTypeMember<WithType>::value << std::endl; // 输出: true std::cout << "WithoutType has Type member: " << HasTypeMember<WithoutType>::value << std::endl; // 输出: false return 0; } |
SFINAE工具
std::enable_if
C++标准库中的一个元编程工具,用于在编译时根据条件选择模板函数或类的有效性- 通常与
SFINAE(Substitution Failure Is Not An Error)机制一起使用,以实现条件编译和类型约束
- 通常与
- 定义
- 定义在头文件
<type_traits>中 - 当模板参数
B为true时,enable_if提供一个名为type的成员类型定义,它的值是T - 当模板参数
B为false时,enable_if没有成员类型type,因此模板实例化失败,从而实现SFINAE - 如果没有显式指定
T,则默认值为void
- 定义在头文件
|
1 2 3 4 5 6 7 8 9 |
namespace std { template<bool B, class T = void> struct enable_if {}; template<class T> struct enable_if<true, T> { typedef T type; }; } |
- 没有指定类型,默认
void,指定了,用指定的类型:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
#include <iostream> #include <type_traits> // 主模板,添加了一个默认的返回类型参数 R template<typename T, typename R = void> typename std::enable_if<std::is_same<T, int>::value, R>::type print(T value) { std::cout << "Type is int: " << value << std::endl; } int main() { // 默认情况下,返回类型为 void print(42); // 输出: Type is int: 42 // 显式指定模板参数 T 和返回类型 R print<int, std::string>(42); // 输出: Type is int: 42 // 显式指定模板参数 T 和返回类型 R // print<double, void>(3.333); // 编译错误,因为 double 不是 int return 0; } |
std::is_same
- 用于判断两个类型是否相同
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#include <iostream> #include <type_traits> template<typename T> typename std::enable_if<std::is_same<T, int>::value, void>::type print(T value) { std::cout << "Type is int: " << value << std::endl; } template<typename T> typename std::enable_if<!std::is_same<T, int>::value, void>::type print(T value) { std::cout << "Type is not int: " << value << std::endl; } int main() { print(42); // 输出: Type is int: 42 print(3.14); // 输出: Type is not int: 3.14 return 0; } |
std::is_integral
- 用于判断类型是否为整型
std::is_floating_point
- 用于判断类型是否为浮点型
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#include <iostream> #include <type_traits> template<typename T> typename std::enable_if<std::is_integral<T>::value, void>::type print(T value) { std::cout << "Integral type: " << value << std::endl; } template<typename T> typename std::enable_if<std::is_floating_point<T>::value, void>::type print(T value) { std::cout << "Floating point type: " << value << std::endl; } int main() { print(42); // 输出: Integral type: 42 print(3.14); // 输出: Floating point type: 3.14 return 0; } |
std::void_t
|
1 2 3 4 |
namespace std { template<typename...> using void_t = void; } |
- 用于简化基于
SFINAE的检测机制 - 它的作用是将一系列类型转换为
void,用于在编译时进行条件检查
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
#include <iostream> #include <type_traits> template<typename, typename = std::void_t<>> struct HasTypeMember : std::false_type {}; // 如果T有一个名为Type的类型成员,特化为true template<typename T> struct HasTypeMember<T, std::void_t<typename T::Type>> : std::true_type {}; // 示例类,有Type成员 class WithType { public: using Type = int; }; // 示例类,没有Type成员 class WithoutType {}; int main() { std::cout << std::boolalpha; std::cout << "WithType has Type member: " << HasTypeMember<WithType>::value << std::endl; // 输出: true std::cout << "WithoutType has Type member: " << HasTypeMember<WithoutType>::value << std::endl; // 输出: false return 0; } |
- 解析如下:
template<typename, typename = std::void_t<>>- 定义了一个模板结构
has_type_member,并且它接受两个模板参数 - 第一个模板参数是一个类型参数,未命名
- 第二个模板参数是一个类型参数,默认值为
std::void_t<> - 在这个上下文中,
std::void_t<>实际上是void - 为什么?
- 因为,在
std::void_t中不传递任何类型参数时,即使用std::void_t<>,相当于将一个空参数包传递给std::void_t,这意味着std::void_t<>就是void。 - 如果传了类型参数给
std::void_t呢? - 传递类型参数给
std::void_t时,using void_t = void;仍然会生效
std::void_t的作用是将任何传递给它的类型参数都转换为void
无论传递什么类型参数,std::void_t都会返回void
- 定义了一个模板结构
struct has_type_member : std::false_type {};- 定义了一个模板结构
has_type_member的默认版本 - 默认版本继承自
std::false_type,这意味着在没有其他特化的情况下,has_type_member默认值为false
- 定义了一个模板结构
struct has_type_member<T, std::void_t<typename T::type>> : std::true_type {};- 定义了
has_type_member的特化版本,当T类型有一个名为type的成员类型时,该特化才有效
- 定义了
std::void_t<typename T::type>:- 如果
T有一个成员类型type,则std::void_t将成功替换并生成void类型 - 如果
T没有type成员类型,则替换失败,特化版本不适用
- 如果
: std::true_type:- 如果替换成功,特化版本继承自
std::true_type,表示值为true。
- 如果替换成功,特化版本继承自
- 上面代码结合在一起,我们可以使用
has_type_member检测一个类型是否具有特定的成员类型
std::is_convertible
- 用于判断一个类型是否可以隐式转换为另一个类型
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
#include <iostream> #include <type_traits> template<typename From, typename To> typename std::enable_if<std::is_convertible<From, To>::value, void>::type printConversionPossible(From from, To to) { std::cout << "Conversion possible from " << from << " to " << to << std::endl; } template<typename From, typename To> typename std::enable_if<!std::is_convertible<From, To>::value, void>::type printConversionPossible(From, To) { std::cout << "Conversion not possible" << std::endl; } int main() { printConversionPossible(42, 3.14); // 输出: Conversion possible from 42 to 3.14 printConversionPossible(3.14, 42); // 输出: Conversion possible from 3.14 to 42 printConversionPossible("hello", 42); // 输出: Conversion not possible return 0; } |
std::is_base_of
- 用于判断一个类型是否是另一个类型的基类
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#include <iostream> #include <type_traits> class Base {}; class Derived : public Base {}; class Unrelated {}; template<typename Base, typename Derived> typename std::enable_if<std::is_base_of<Base, Derived>::value, void>::type checkInheritance() { std::cout << "Derived is derived from Base" << std::endl; } template<typename Base, typename Derived> typename std::enable_if<!std::is_base_of<Base, Derived>::value, void>::type checkInheritance() { std::cout << "Derived is not derived from Base" << std::endl; } int main() { checkInheritance<Base, Derived>(); // 输出: Derived is derived from Base checkInheritance<Base, Unrelated>(); // 输出: Derived is not derived from Base return 0; } |
std::conditional
- 根据条件选择一种类型
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
#include <iostream> #include <type_traits> // 根据条件选择类型 template<bool Condition, typename TrueType, typename FalseType> using ConditionalType = typename std::conditional<Condition, TrueType, FalseType>::type; int main() { ConditionalType<true, int, double> trueType; // int ConditionalType<false, int, double> falseType; // double std::cout << "trueType is int: " << std::is_same<decltype(trueType), int>::value << std::endl; // 输出: true std::cout << "falseType is double: " << std::is_same<decltype(falseType), double>::value << std::endl; // 输出: true return 0; } |
std::is_arithmetic
- 用于判断一个类型是否为算术类型(整数或浮点数)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
#include <iostream> #include <type_traits> template<typename T> typename std::enable_if<std::is_arithmetic<T>::value, void>::type printArithmetic(T value) { std::cout << "Arithmetic type: " << value << std::endl; } template<typename T> typename std::enable_if<!std::is_arithmetic<T>::value, void>::type printArithmetic(T value) { std::cout << "Non-arithmetic type: " << value << std::endl; } int main() { printArithmetic(42); // 输出: Arithmetic type: 42 printArithmetic(3.14); // 输出: Arithmetic type: 3.14 printArithmetic("Hello"); // 输出: Non-arithmetic type: Hello return 0; } |
概念
概述
- 概念(
C++20引入):用于约束模板参数,使得模板编程更加安全和易读 - 允许更明确地定义模板的适用范围,提高模板代码的可读性和错误信息的可理解性
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#include <concepts> #include <iostream> template<typename T> concept Incrementable = requires(T t) { { t++ } -> std::same_as<T&>; }; template<Incrementable T> void increment(T& t) { t++; } int main() { int x = 42; increment(x); // OK std::cout << x << std::endl; // std::string s = "Hello"; // increment(s); // Error: std::string does not satisfy Incrementable return 0; } |
定义概念
- 模板参数
T:template<typename T>声明了一个模板参数T,表示我们将对类型T进行约束检查 - 概念
Incrementable:concept Incrementable声明了一个概念,类似于一个编译时的断言,用于检查类型T是否满足特定条件 requires子句:requires(T t)是一个要求表达式,表示接下来的条件必须满足- 要求表达式:
{ t++ } -> std::same_as<T&>;{ t++ }:要求类型T的对象t可以进行后置自增操作(即t++必须是有效的)-> std::same_as<T&>:后置自增操作的结果类型必须是T&。也就是说,t++的返回类型必须是对T类型的左值引用
|
1 2 3 4 |
template<typename T> concept Incrementable = requires(T t) { { t++ } -> std::same_as<T&>; }; |
使用概念约束模板参数
template<Incrementable T>声明了模板参数T,并用概念Incrementable对其进行约束- 这意味着只有满足
Incrementable概念的类型才能用于这个模板
- 这意味着只有满足
|
1 2 3 4 |
template<Incrementable T> void increment(T& t) { t++; } |
声明:本文为原创文章,版权归Aet所有,欢迎分享本文,转载请保留出处!
你可能也喜欢
- ♥ C++编程规范101规则、准则与最佳实践 二01/07
- ♥ C++_PIMPL 模式07/13
- ♥ C++_关于函数调用过程10/30
- ♥ 51CTO:C++编程技巧与规范08/01
- ♥ C++并发编程 _ 无锁数据结构09/18
- ♥ C++11_第一篇12/01