在这里插入图片描述

📃个人主页:island1314

⛺️ 欢迎关注:👍点赞 👂🏽留言 😍收藏 💞 💞 💞

  • 生活总是不会一帆风顺,前进的道路也不会永远一马平川,如何面对挫折影响人生走向 – 《人民日报》


官方文档:https://en.cppreference.com/w/cpp/language/constraints.html

一、概念


1.1 Concepts 是什么?

“A concept is a named set of requirements.”
概念是一个命名的需求集合

🔑 核心思想

  • 传统模板:编译时"试错" → 报错信息晦涩难懂
  • Concepts:编译时"契约" → 提前约束 + 清晰报错
    • 定义:概念(concept)是C++20引入的模板参数约束机制,它允许程序员明确指定模板参数必须满足的条件。概念本质上是一个 编译时谓词,用于验证模板参数是否满足特定要求。
    • 作用:概念会在 模板实例化前检查类型是否满足条件,而不是在实例化后产生难以理解的错误。

注意:概念的定义必须出现在命名空间范围内

1.2 语法结构拆解

1️⃣ 基本语法形式

template <template-parameter-list>
concept concept-name attr(optional) = constraint-expression;
组成部分说明示例
template<...>模板参数列表<class T>
concept关键字固定写法
concept-name概念名称Integral, Derived
constraint-expression约束表达式std::is_integral_v<T>
  • 不允许对概念进行显式实例化、显式特化或部分特化(不能改变约束的原始定义的含义)。

  • 可以在 ID 表达式中命名概念。

  • ID 表达式的值为:如果约束表达式得到满足 true ,否则 false

  • 概念也可以在类型约束中命名,作为……的一部分

    • 类型模板参数声明

    • 占位符类型说明符

    • 化合物需求

类型约束中,概念 的模板参数比其参数列表要求的少一个,因为上下文推断的类型被隐式地用作概念的第一个参数。

2️⃣ 示例解析

示例 1:基础概念定义

template<class T, class U>
concept Derived = std::is_base_of<U, T>::value;
  • 含义Derived<T, U>true 当且仅当 TU 的派生类
  • 使用场景:约束模板参数必须满足继承关系
  • 底层原理:Concept 本质是一个编译期布尔谓词

示例 2:递归概念(非法)

template<typename T>
concept V = V<T*>; // error: recursive concept
  • 原因:Concept 在编译期求值,递归会导致无限展开
  • 类比:类似 constexpr 函数不能无限递归

示例 3:约束概念定义(非法)

template<class T>
concept C1 = true;

template<C1 T>           // 错误:不能用 C1 约束概念定义
concept Error1 = true;

template<class T> requires C1<T>  // 错误:同样不允许
concept Error2 = true;
  • 核心规则Concept 定义本身不能被约束
  • 设计哲学:Concept 是"元约束",用于约束普通模板,而非约束自己

示例 4:Concept 的使用方式

template<Derived<Base> T>
void f(T); // 等价于:requires Derived<T, Base>
  • 关键技巧:在 type-constraint 中,Concept 少写一个参数
  • 隐式规则Derived<Base> T → 自动推导为 Derived<T, Base>
  • 记忆口诀“占位类型 T 自动补到最前面”

1.3 Concepts 的使用场景

场景 1:简单使用

// 方式1:使用标准库类型萃取 
template<typename T>
concept Integral = std::is_integral_v<T>;

// 方式2:简单的常量表达式 
template<typename T>
concept SmallType = sizeof(T) <= 4;

调用如下:

image-20260221223547692

场景 2:函数模板约束(最常用)

// ✅ 方式 1:abbreviated function template(简洁)
std::integral auto add(std::integral auto a, std::integral auto b) {
    return a + b;
}

// ✅ 方式 2:requires 子句(灵活)
template<typename T>
requires std::integral<T>
T multiply(T a, T b) { return a * b; }

// ✅ 方式 3:concept 命名约束(可复用)
template<typename T>
concept Number = std::integral<T> || std::floating_point<T>;

template<Number T>
T compute(T x) { return x * x; }

场景 3:类模板约束

template<typename T>
requires std::copyable<T>
class Buffer {
    std::vector<T> data;
public:
    void push(T item) { data.push_back(std::move(item)); }
};

// 或简写:
template<std::copyable T>
class Queue { /*...*/ };

场景 4:概念组合与逻辑运算

template<typename T>
concept Printable = requires(T t) {
    { std::cout << t } -> std::convertible_to<std::ostream&>;
};

template<typename T>
concept Serializable = Printable<T> && requires(T t) {
    { t.to_json() } -> std::convertible_to<std::string>;
};

template<Serializable T>
void save(const T& obj); // 必须同时支持打印 + 序列化

1.4 Concepts 如何工作?

编译期求值流程

  1. 编译器遇到模板实例化请求
  2. 检查关联的 concept 约束表达式
  3. 若表达式为 true → 继续实例化
  4. 若表达式为 false → 丢弃该重载(SFINAE)或报错
  5. 若无匹配重载 → 给出清晰的"constraint not satisfied"错误

🎁 附:Concepts 速查卡片

// 🔹 标准概念(<concepts>)
std::integral<T>        // 整数类型
std::floating_point<T>  // 浮点类型
std::same_as<T, U>      // 类型完全相同
std::convertible_to<T, U> // 可隐式转换
std::invocable<F, Args...> // 可调用
std::copyable<T>        // 可拷贝(= movable + copy_constructible)

// 🔹 自定义概念模板
template<typename T>
concept HasSize = requires(T t) {
    { t.size() } -> std::convertible_to<std::size_t>;
};

// 🔹 组合逻辑
template<typename T>
concept Container = HasSize<T> && requires(T t) {
    { t.begin() } -> std::input_or_output_iterator;
};

// 🔹 使用方式
template<Container C>
void print_size(const C& c) {
    std::cout << c.size() << "\n";
}

💬 小结

Concepts 的本质:把"运行时文档"提前到"编译时契约"。

它不是银弹,但对模板库作者接口设计者系统编程者(比如你)来说,是提升代码质量、降低维护成本的利器。

1.5 SFINAE 和 Concepts

  • SFINAE 是 Substitution Failure Is Not An Error 的缩写,翻译为替换失败不是错误,通过模板替换失败静默排除候选函数,实现编译期条件分支。本质是技巧性机制,依赖模板实例化规则如 std::enable_ifdecltype
  • Concepts 直接声明模板参数的语义约束(如“类型必须可比较”),提供显式接口描述。目标是提升代码可读性和编译错误信息
  • SFINAE 代码冗长且晦涩,Concepts 是SFINAE的官方替代方案,解决了后者在可维护性和表达力上的缺陷。
特性C++17 及之前C++20 Concepts
模板约束SFINAE / enable_if(黑魔法)requires / concept(白魔法✨)
报错信息“no type named ‘type’ in…” 😵“constraint not satisfied: ‘Integral’” 🎯
代码意图隐藏在模板元编程中直接写在函数签名上
可读性专家友好新人友好
// C++17:使用 enable_if 约束整型参数 
template<typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
void foo(T x) {}

// C++20:使用 concept 约束 
template<std::integral T>
void foo(T x) {}

二、约束

2.1 约束本质

Constraints(约束),这是 Concepts 的"灵魂"。简单来说:

Concept 是命名的约束,Constraint 是具体的布尔表达式。

📚 约束的本质:编译期的"契约检查"

核心关系图

Constraint Expression(约束表达式)
        ↓
    [求值] → true/false(编译期)
        ↓
   Concept(命名的约束) ← 可复用、可组合
        ↓
Template/Function/Class(被约束的目标)

约束 vs 概念 vs SFINAE

特性SFINAE (enable_if)Concept + Constraint
表达位置模板参数默认值/返回类型函数签名/requires 子句
可读性typename = std::enable_if_t<...>requires std::integral<T>
组合能力⭐ 嵌套模板元编程⭐⭐⭐ && `
报错信息❌ “substitution failure”✅ “constraint not satisfied: ‘Packet’”
重载解析⭐ 需手动控制优先级⭐⭐⭐ 自动按"更特化"排序

2.2 约束的基本形式

1️⃣ 原子约束(Atomic Constraints)

最基础的布尔表达式,通常是 std::is_xxxrequires 表达式:

// 类型特质
std::is_integral_v<T>
std::is_pointer_v<T>

// 概念实例化
std::same_as<T, int>
MyConcept<T, U>

// requires 表达式(重点!)
requires(T t) {
    { t.size() } -> std::convertible_to<std::size_t>;
    { t.begin() } -> std::input_iterator;
}

💡 关键:原子约束是约束的"最小单元",编译器会对它们进行正规化(normalization) 以便比较。

2️⃣ 逻辑组合约束

&& || ! 组合原子约束,形成复杂条件:

  • **合取 && **:逻辑与,所有约束都必须为true,整个合取约束才为true,使用&&运算符连接多个原子约束。
  • **析取 || **:逻辑或,多个约束中至少有一个为true,整个析取约束就为true,使用运算符连接多个约束。
// 1. 逻辑与(&&) 
template<typename T>
concept IntegralAndPrintable = std::integral<T> && requires(T t) {
 std::cout << t;
};
// 2. 逻辑或(||) 
template<typename T>
concept Number = std::integral<T> || std::floating_point<T>;
// 3. 逻辑非(!) 
template<typename T>
concept NonPointer = !std::is_pointer_v<T>;

3️⃣ Requires 表达式(最强大!)

用于检查表达式是否合法,而非仅仅类型特质:

#include <concepts>
#include <iostream>
#include <vector>
#include <string>
// 1、简单要求  
// 最简单的形式,只检查表达式是否合法(能否编译通过) 
template<typename T>
concept Addable = requires(T a, T b) {
	a + b; // 检查类型T是否支持+运算 
};

// 2、类型要求
// 检查某个嵌套类型是否存在 
template<typename T>
concept HasValueType = requires {
	typename T::iterator;	// 检查T是否有iterator嵌套类型 
	typename T::value_type; // 检查T是否有value_type嵌套类型 
};

// 3、复合要求 
// 不仅检查表达式是否合法,还可以指定返回类型和异常规范: 
// 复合要求的完整语法为: 
// { 表达式 }[noexcept]->类型约束; 
template<typename T>
concept ConvertibleAddable = requires(T a, T b) {
	{ a + b } -> std::convertible_to<T>; // 要求a+b的结果可转换为T 
	{ a += b } noexcept -> std::same_as<T&>; // 要求a+=b不抛异常且返回T& 
};


// 4、嵌套要求 
// 在 requires 内部再使用 requires 进行更复杂的约束 
// requires (参数列表) { 
// //... 其它要求 (如类型要求、复合要求等) 
// ...requires <常量布尔表达式>; 
// }
template<typename T>
concept ComplexConcept = requires(T t) {
	requires sizeof(T) <= sizeof(long);
	requires std::is_class_v<T>;
};

template<Addable T>
void f1(const T& x1, const T& x2)
{
	T ret = x1 + x2;
	std::cout << ret << std::endl;
}

template<HasValueType T>
void f2(const T& x){}

template<ConvertibleAddable T>
void f3(T& x1, const T& x2)
{
	T ret = x1 + x2;
	std::cout << ret << std::endl;
	x1 += x2;
	std::cout << x1 << std::endl;
}

template<ComplexConcept T>
void f4(const T& x){}


int main()
{
	f1(1, 2);
	// f1("xxx", "yyy");
	
	f2(std::vector<int>{1, 2, 3});
	// f2(1);
	std::string s1("111");
	
	// f3(s1, std::string("222"));
	int i = 1;
	f3(i, 2);
	
	// f4(1);
	// f4(std::vector<int>{1, 2, 3});
	f4(std::less<int>());
	return 0;
}

Requires 表达式的语法细节

requires (参数列表可选) {
    要求 1;
    要求 2;
    ...
}
要求形式含义示例
{ expr }表达式必须合法{ t.size() }
{ expr } -> type-concept表达式合法 + 返回类型满足概念{ t.begin() } -> std::input_iterator
requires { ... }嵌套约束requires { typename T::value_type; }
typename T::X检查嵌套类型是否存在typename Packet::header_t

补充:折叠扩展约束,可变参数包的约束扩展,对参数包的多个参数进行包扩展的约束,C++26才开始支持。

2.3 约束的使用场景

场景 1:函数模板约束(最常用)

#include <iostream>
#include <string>
#include <vector>
#include <concepts>

// 定义一个要求T是整形的概念 
template< class T >
concept Integral = std::is_integral_v<T>;
template< class T >
concept Float = std::is_floating_point_v<T>;
// 简单的requires⼦句 
template<typename T>
concept Incrementable = requires(T t) {
 	++t; // 检查前缀++是否有效 
 	t++; // 检查后缀++是否有效 
};


// 1、模板参数后直接使用
template<Integral T>
void f1(T x){
	std::cout << "有 concepts 约束" << std::endl;
}

// 2、requires 子句
template<typename T>
requires Integral<T> || Float<T>
void f2(T x) {}

// 3、尾置requires 子句
template<typename T>
void f3(T x) requires Integral<T> || Float<T> {}

// 4、临时约束 - 使用 requires requires 
template<typename T>
requires requires(T x) { x.size(); x + x; }
void f4(T x) {}

// 5、auto占位符约束 
void f5(Integral auto x) {}

int main(){
	
	f1(1);			// 符合concept要求则实例化调用对应函数模板 
	//f1("xxx");	// 不符合concept要求,报错 
	return 0;
}

场景 2:类模板约束(接口设计)

// 约束:T 必须可拷贝 + 可序列化
template<std::copyable T>
requires requires(const T& obj, std::vector<std::byte>& buf) {
    { obj.serialize(buf) } -> std::same_as<bool>;
}
class MessageQueue {
    std::deque<T> queue;
public:
    void push(const T& item) {
        if constexpr (requires { item.validate(); }) {
            if (!item.validate()) throw invalid_message{};
        }
        queue.push_back(item);
    }
};

2.4 约束的求值规则(编译器如何工作)

1. 正规化(Normalization)

编译器会把复杂约束表达式拆解为原子约束的集合,便于比较和排序:

// 原始约束
requires (std::integral<T> && std::signed_integral<T>) || std::floating_point<T>

// 正规化后(伪代码)
{
  { std::integral<T>, std::signed_integral<T> },  // AND 组
  { std::floating_point<T> }                       // OR 组
}

2. 蕴含(Subsumption)→ 重载解析的关键

约束 A 蕴含 约束 B:当 A 为 true 时,B 一定为 true。

template<typename T>
concept Integral = std::integral<T>;

template<typename T>
concept SignedIntegral = Integral<T> && std::is_signed_v<T>;

// 重载函数
template<Integral T> void process(T);           // #1
template<SignedIntegral T> void process(T);     // #2

process(42);   // 调用 #2:SignedIntegral<int> 蕴含 Integral<int>,更特化优先
process(42u);  // 调用 #1:unsigned 不满足 SignedIntegral

3. 偏序(Partial Ordering) —> 重点

概念:C++20概念的 约束偏序规则 用于在函数重载或模板特化时,判断哪个约束的模板 “更特化” 或 “更受约束”。编译器使用这个规则来选择最合适的模板。

当多个重载都满足约束时,编译器按**“更特化”** 原则,偏序规则的核心是

  1. 如果约束A包含(蕴含)约束B,那么A比B"更受约束"。
  2. 这意味着任何满足A的类型也一定会满足B,但满足B的类型不一定满足A。即 A = > B A=>B A=>B (A 蕴含 B)
  3. 当两个模板匹配时,编译器会选择"更受约束"的那一个。
#include <iostream>
#include <concepts>

// 定义一个要求T是整形的概念 
template< class T >
concept Integral = std::is_integral_v<T>;

// 1、模板参数后直接使用
template<Integral T>
void f(T x){
	std::cout << "有 concepts 约束" << std::endl;
}

template<class T>
void f(T x){
	std::cout << "无 concepts 约束" << std::endl;
}

template <class T> concept A = std::is_move_constructible_v<T>;
template <class T> concept B = std::is_copy_constructible_v<T>;
template <class T> concept C = A<T> && B<T>;

template <class T>
requires A<T> void g(T x){
	std::cout << "g()->A" << std::endl;
}
template <class T>
requires C<T> void g(T x){
	std::cout << "g()->C" << std::endl;
}

int main(){
	// 符合concept要求则实例化调用对应的函数模板 
	f(1);
	// 不符合concept要求,查找无concept要求的模板实例化调用,没找到则报错 
	f("xxx");
	
	g(1.1);						// 这里同时满足A和B,会优先匹配C 
	g(std::unique_ptr<int>()); 	// 这里不满足C,只满足A,会匹配A 
	return 0;
}

2.5 常见问题

陷阱 1:约束"太松"导致意外匹配

template<typename T>
concept HasSize = requires(T t) { t.size(); };  // ❌ 只检查表达式存在,不检查返回类型

template<HasSize T>
void print_size(const T& c) {
    std::cout << c.size();  // 如果 size() 返回 std::string 而非 size_t?编译通过但逻辑错误!
}

// ✅ 改进:加上返回类型约束
template<typename T>
concept HasSize = requires(T t) {
    { t.size() } -> std::convertible_to<std::size_t>;
};

陷阱 2:约束"太紧"导致无法扩展

// ❌ 直接写死类型
template<typename T>
concept MyInt = std::same_as<T, int>;  // 无法支持 long, short 等

// ✅ 用标准概念组合
template<typename T>
concept MyInt = std::integral<T> && (sizeof(T) <= sizeof(int));

陷阱 3:requires 表达式中的"隐式转换"

template<typename T>
concept Addable = requires(T a, T b) {
    { a + b };  // ✅ 只要表达式合法即可,不检查返回类型
};

template<typename T>
concept AddableStrict = requires(T a, T b) {
    { a + b } -> std::same_as<T>;  // ✅ 额外要求:返回类型必须是 T
};

🔍 调试技巧

技巧 1:静态断言 + 概念

static_assert(std::integral<int>);  // 编译期验证概念定义
static_assert(!std::integral<double>);

技巧 2:概念"打印"(C++20 不支持 runtime,但可借助模板元编程)

template<typename T>
struct concept_check {
    static constexpr bool is_integral = std::integral<T>;
    static constexpr bool is_pointer = std::is_pointer_v<T>;
};

// 使用:
if constexpr (concept_check<MyType>::is_integral) {
    // ...
}

技巧 3:Compiler Explorer 查看约束求值

访问 godbolt.org,选择 MSVC 19.38,开启 -std:c++20,观察:

  • 约束失败时的报错信息
  • 重载解析时哪个函数被选中

⚠️ 注意事项

  1. IntelliSense 延迟:复杂 requires 表达式可能导致 VS 提示变慢
    • 解决:临时关闭"增强型 IntelliSense",或用 #if INTELLISENSE 条件编译简化
  2. 模块 + 约束:若用 import std;,确保 <concepts> 已导出(VS 2022 17.8+ 已支持)
  3. 调试信息:开启 /Zi 后,约束失败会在调用栈中显示 concept 名称

🎁 附:约束速查卡片

// 🔹 原子约束示例
std::is_same_v<T, int>
std::derived_from<T, Base>
requires(T t) { t.foo(); }

// 🔹 逻辑组合
concept A = X<T> && Y<T>;
concept B = X<T> || Y<T>;
concept C = !X<T>;

// 🔹 requires 表达式模板
template<typename T>
concept MyConcept = requires(T t) {
    // 检查表达式
    { t.method() };
    
    // 检查表达式 + 返回类型
    { t.get_value() } -> std::convertible_to<int>;
    
    // 检查嵌套类型
    typename T::value_type;
    
    // 嵌套 requires
    requires std::is_copy_constructible_v<T>;
};

// 🔹 使用方式汇总
template<typename T> requires MyConcept<T>  // 前置 requires
void f1(T);

template<typename T>
void f2(T) requires MyConcept<T>;           // 后置 requires

template<MyConcept T>                        // concept 作为类型约束
void f3(T);

MyConcept auto f4(MyConcept auto x);        // abbreviated function template

三、两者关系

类模板函数模板(包括通用 lambda 表达式)和其他模板化函数(通常是类模板的成员)可以与约束 相关联,该约束规定了对模板参数的要求,可用于选择最合适的函数重载和模板特化。

此类需求的命名集合称为概念 。每个概念都是一个谓词,在编译时进行求值,并成为模板接口的一部分,用作约束条件:

#include <cstddef> 
#include <concepts> 
#include <functional> 
#include <string>
 
// 声明概念“Hashable”,任何类型“T”都满足该概念
// 使得对于类型为“T”的值“a”,表达式 std::hash<T>{}(a) 
// 可以编译,并且其结果可以转换为 std::size_t 
template < typename T > 
concept Hashable = requires ( T a ) 
{ 
    {  std:: hash < T > { } ( a )  }  - >  std:: convertible_to < std:: size_t > ; 
} ;
 
struct meow { } ;
 
// 受约束的 C++20 函数模板:
template < Hashable T > 
void f ( T )  { } 
// 
// 应用相同约束的其他方法:
// template<typename T> 
// requires Hashable<T> 
// void f(T) {} 
// 
// template<typename T> 
// void f(T) requires Hashable<T> {} 
// 
// void f(Hashable auto /* parameter-name */) {}
 
int main ( ) 
{ 
    using std :: operator "" s ;
 
    f ( "abc" s ) ;     // OK,std::string 满足 Hashable 条件
    // f(meow{}); // 错误:meow 不满足 Hashable 条件
}

违反约束条件的情况会在编译时,也就是模板实例化过程的早期阶段被检测到,从而生成易于理解的错误消息:

std:: list <int> l = { 3 , -1 , 10 } ; 
std:: sort ( l.begin() , l.end()) ; //典型的编译器诊断(不包含概念):
//二元表达式的操作数无效('std::_List_iterator<int>' and // 'std::_List_iterator<int>') 
// std::__lg(__last - __first) * 2); 
// ~~~~~~ ^ ~~~~~~~ 
// ... 50 行输出 ... // 
// 典型的编译器诊断(包含概念):// 错误:无法使用 std::_List_iterator<int> 调用 std::sort 
// 注意:概念 RandomAccessIterator<std::_List_iterator<int>> 未满足 

概念的目的是对语义类别(例如数字、范围、常规函数)进行建模,而不是对语法限制(例如有加、数组)进行建模。根据ISO C++ 核心指南 T.20,“能够指定有意义的语义是真正概念的决定性特征,而不是语法约束。”

综合实践样例

① 实践中解决哈希表对key参数要求的问题

#include <cstddef>
#include <concepts>
#include <functional>
#include <string>

// 概念 HashType 的声明可以被符合以下条件的任意类型 T 满⾜:
// 对于 T 类型的值 a,表达式 std::hash<T>{}(a) 可以编译并且它的结果可以转换到
std::size_t
template<typename T>
concept HashType = requires(T a)
{
	{ std::hash<T>{}(a) } -> std::convertible_to<std::size_t>;
};

// 受约束的 C++20 类模板
template<HashType K, class HashFunc = std::hash<K>>
class hashmap
{ 
public:
	bool insert(const K& key)
	{
		size_t hashi = HashFunc()(key);
	}
};

int main()
{
	hashmap<int> hm1;
	hashmap<std::string> hm2;
	//hashmap<std::pair<std::string, int>> hm3;
	return 0;
}

② 多种复杂条件组合和嵌套的

#include <concepts>
#include <iostream>
#include <vector>
#include <mutex>

template<typename T>
concept PrintableRange = requires(T t) {
	{ t.begin() }->std::same_as<typename T::iterator>;
	{ t.end() }->std::same_as<typename T::iterator>;
	
	requires std::integral<typename T::value_type>;
	
	// 对T内部的内容的要求,所以要在嵌套定义⼀个requires的参数
	requires requires(typename T::value_type e) { std::cout << e; };
};

template<PrintableRange R>
void print_range(const R& r) {
	for (const auto& e : r) {
		std::cout << e << ' ';
	} 
	std::cout << '\n';
} 

int main() {
	std::vector<int> v{ 1, 2, 3 };
	print_range(v); // OK
	
	std::vector<std::mutex> vm;
	print_range(vm); // 错误: 不满⾜PrintableRange
	return 0;
}

③ 使用概念解决一些模板匹配问题

#include <concepts>
#include <iostream>

template<typename T>
concept IteratorType = requires(T it) {
	// 要求只支持前置++和解引⽤
	++it;
	*it;
};

namespace bit
{
template<class T>
class vector
{ 
public:
    typedef T* iterator;

    // 强制编译器⽣成默认构造
    vector() = default;

    // 函数模板
    // 任意类型容器迭代器初始化
    //template <IteratorType InputIterator>
    template <typename InputIterator>
    vector(InputIterator first, InputIterator last){
        while (first != last){
            push_back(*first);
            ++first;
        }
    } 

    vector(size_t n, const T& val = T()){}
    void push_back(const T& x){}
private:
    iterator _start = nullptr;
    iterator _finish = nullptr;
    iterator _endofstorage = nullptr;
};
} 

int main()
{
	bit::vector<int> v7(10, 1);
	bit::vector<size_t> v9(10, 1);
	
	return 0;
}

★,°:.☆( ̄▽ ̄)/$:.°★ 】那么本篇到此就结束啦,如果有不懂 和 发现问题的小伙伴可以在评论区说出来哦,同时我还会继续更新关于【C++】的内容,请持续关注我 !!

在这里插入图片描述

Logo

助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

更多推荐