C++20新特性_概念 (Concepts)

本文记录C++20新特性之概念(Concepts)。

文章目录

  • [第一章 C++20核心语法特性](#第一章 C++20核心语法特性)
    • [1.1 概念 (Concepts)](#1.1 概念 (Concepts))
      • [1.1.1 Concepts实现原理](#1.1.1 Concepts实现原理)
      • [1.1.2 concepts的使用举例](#1.1.2 concepts的使用举例)
        • [示例1 指定参数类型](#示例1 指定参数类型)
        • 示例2:约束成员函数
        • [示例3:基于 Concept 的函数重载](#示例3:基于 Concept 的函数重载)
      • [1.1.3 总结](#1.1.3 总结)

第一章 C++20核心语法特性

1.1 概念 (Concepts)

概念是C++20引入的新特性,用于模板编程中,被认为是C++11引入auto和lambda以来,模板编程中的最大变革。

C++20之前,模板编程(泛型编程)虽然强大,但存在两个著名的痛点:

缺点1: 不能精确定位错误位置:当向模板中传递一个不支持的类型时,编译器出现报错,但是不能精确指向哪一行,并支持错误。

缺点2:对预定类型的约束。在泛型编程时,想要指定某个函数模板T只能支持整型 或者 参数T必须有成员函数 hash(),通常使用SFINAE特性或enable_if来实现约束,这实现起来比较麻烦,请看《C++模板与泛型编程》笔记。

C++20引入了Concepts,就是为了给模板参数加上语义约束,如果参数传递错误,编译器给出清晰的错误提示,可轻松解决上面两个问题。

1.1.1 Concepts实现原理

concepts 本质上是编译期的谓词,用来约束类型,如果类型满足则返回true, 否则返回false.

语法:

concept: 定义概念

requires : 在模板中使用概念进行约束。

concepts的两个主要作用:

1 约束模板参数:明确指定模板参数必须满足的条件(如必须是整数、必须可排序)。

2 改善错误信息:如果约束不满足,编译器提示"类型不满足"这样的错误。

1.1.2 concepts的使用举例

示例1 指定参数类型

编写一个函数,必须接收int或浮点数类型的参数,不接收其他类型,实现如下:

cpp 复制代码
    // 1. 定义一个 Concept
    // T 必须是整数或浮点数
    template <typename T>
    concept Number = std::integral<T> || std::floating_point<T>;

    // 2. 使用 Concept 约束函数参数
	// 方式1:使用requires 关键字
    template<typename T>
		requires Number<T>
    void add(T a, T b)
    {
		std::cout << "Sum: " << (a + b) << std::endl;
    }

    // 写法B : 发直接替换typename 简洁
    void sub(Number auto a, Number auto b)
    {
		std::cout << "Difference: " << (a - b) << std::endl;
    }

    void test()
    {
        add(1, 2);
        sub(1, 2);

        // 传入字符串
       // add("aa", "bb");
        // "sp28::add": 未找到匹配的重载函数
    }
示例2:约束成员函数

在写模板编程时,要求类型T必须有一个hash()函数,且返回值为size_t.

cpp 复制代码
	// 假设要求类型T必须有一个hash()函数,且返回值为size_t
    template<typename T>
    concept Hashable = requires(T a) 
    {
        { a.hash() } -> std::same_as<size_t>;
        // 表达式 a.hash() 必须合法,且返回值必须能转换为 size_t
	};

    struct MyData 
    {
        size_t hash() const 
        {
            return 42; // 示例实现
        }
	};

    // 对象2 
    struct BadData 
    {
        int value;
        // 没有 hash() 方法
        void hash()
        {
            cout << "没有返回值的hash()" << endl;
        }
	};

	// 使用concept 约束函数
    void process(Hashable auto const& item)
    {
		std::cout << "Hash " << item.hash() << std::endl;
    }

    void test()
    {
        MyData d;
        process(d); // 合法
        // Hash 42

        BadData bd;
		//process(bd); // 编译错误,BadData 不满足 Hashable
        // 编译错误:"process": 未找到匹配的重载函数
    }
示例3:基于 Concept 的函数重载

指定某个重载版本的类型T必须支持随机访问。

cpp 复制代码
// 版本1: 支持任何标准容器
 template<typename T>
 void print(const T& t)
 {
     std::cout << "支持任意类型打印" << endl;
 }

 // 版本2:专门指针 可随机访问的容器
 template<typename T>
		requires std::ranges::random_access_range<T>
 void print(const T& t)
 {
		cout << "支持随机访问容器打印" << endl;
 }

 void test()
 {
		std::list<int> lst = { 1,2,3 };
		std::vector<int> vec = { 4,5,6 };

     print(lst); // 调用版本1
     // 支持任意类型打印
     print(vec); // 调用版本2
     /*
         支持随机访问容器打印
		*/
 }

1.1.3 总结

"concepts" 引入让模板代码更简介、更容易调试。以后写泛型编程时,尽量使用Concept进行约束。

std::ranges::random_access_range 也是C++20引入的新特性,见下。

相关推荐
Mr_WangAndy2 小时前
C++20新特性_范围 (Ranges)
c++20·c++20新特性·c++40周年·范围ranges·视图适配器·视图view
Mr_WangAndy7 小时前
C++20新特性_[[no_unique_address]]属性
c++20·c++20新特性·c++40周年
Mr_WangAndy9 小时前
C++20新特性_模块(Modules)
c++20·c++40周年·c++20新特性模块
Mr_WangAndy10 小时前
C++20新特性_范围 `for` 循环的初始化语句
c++20·c++40周年·范围for初始化
Mr_WangAndy11 小时前
C++20新特性_三路比较运算符 (<=>)
c++20·c++40周年·三路比较运算符
Mr_WangAndy11 小时前
C++20新特性_consteval 和 constinit
c++20·c++40周年·consteval·constinit
Azxcc012 小时前
c++20协程浅析
网络·c++20
Mr_WangAndy12 小时前
C++20新特性_协程(Coroutines)
c++20·c++20新特性·c++40周年·c++20协程
Mr_WangAndy12 小时前
C++20新特性_Lambda 改进
c++20·c++20新特性·c++40周年·lambda表达式改进