泛型非类型模板参数
template
)编译期控制流
constexpr if
替代模板偏特化(减少代码膨胀)反射与元编程增强
is_convertible_v
等)模块化支持
语法简化
typename
使用限制void
类型规范化(统一处理无返回值函数)Concepts(概念)
功能:通过显式约束模板参数类型,提升编译时类型检查和错误提示的可读性。
#include
#include
#include
// 定义一个Concept,要求类型支持加法运算
template<typename T>
concept Addable = requires(T a, T b) {
{ a + b } -> std::same_as<T>;
};
// 使用Concept约束模板参数
template<Addable T>
T add(T a, T b) {
return a + b;
}
int main() {
std::cout << add(1, 2) << std::endl; // 输出3(int满足Addable)
std::cout << add(1.5, 2.5) << std::endl; // 输出4(double满足Addable)
// std::vector不满足Addable(无operator+),编译报错
// std::cout << add(std::vector{1}, std::vector{2}) << std::endl;
return 0;
}
编译命令(需支持C++20):
g++ -std=c++20 concepts.cpp -o concepts
Modules(模块)
功能:替代传统头文件,提升编译速度和封装性。
示例代码:
math_module.ixx(模块接口文件):
export module math;
export template<typename T>
T add(T a, T b) {
return a + b;
}
main.cpp(使用模块):
import math;
int main() {
std::cout << add(3, 5) << std::endl; // 输出8
return 0;
}
编译命令(需支持C++20模块):
g++ -std=c++20 -fmodules-ts -c math_module.ixx -o math_module.o
g++ -std=c++20 -fmodules-ts main.cpp math_module.o -o main
17.6 Deduction for Nonfinal Pack Expansions(非终结包展开推导)
功能:允许在模板参数包展开时进行类型推导。
示例代码:
#include
#include
// 推导包展开中的类型
template<typename... Ts>
auto make_tuple_and_print(Ts&&... args) {
auto t = std::make_tuple(std::forward<Ts>(args)...);
std::cout << "Tuple elements: ";
((std::cout << args << " "), ...);
std::cout << std::endl;
return t;
}
int main() {
auto t = make_tuple_and_print(1, 2.5, "Hello");
// 输出类型:std::tuple
return 0;
}
编译命令:
g++ -std=c++20 pack_deduction.cpp -o pack_deduction
关键知识点总结
特性 | 核心优势 | 典型应用场景 |
---|---|---|
Concepts | 显式约束模板参数,增强编译错误信息 | 泛型算法、类型安全检查 |
Modules | 替代头文件,避免宏污染,提升编译速度 | 大型项目模块化管理 |
包展开推导 | 简化变长模板参数处理,支持复杂类型构造 | 元编程、通用容器实现 |
扩展练习
尝试实现一个使用Concepts约束的Comparable
概念,并编写一个通用的比较函数模板:
#include
#include
template<typename T>
concept Comparable = requires(T a, T b) {
{ a == b } -> std::convertible_to<bool>;
{ a != b } -> std::convertible_to<bool>;
};
template<Comparable T>
bool safe_compare(const T& a, const T& b) {
return a == b;
}
int main() {
std::cout << std::boolalpha;
std::cout << safe_compare(10, 20) << std::endl; // 输出false
std::cout << safe_compare(std::string("Hello"), std::string("World")) << std::endl; // 输出false
// std::vector无法比较,编译报错
// safe_compare(std::vector{1}, std::vector{2});
return 0;
}
多选题
题目1:关于C++20 concept
的描述正确的是?
A. 必须在模板参数列表中使用requires
子句
B. 可以定义默认约束条件
C. 支持逻辑运算符组合约束
D. 能够自动推导约束条件
题目2:constexpr if
与普通if
的关键区别是?
A. 编译期求值条件
B. 支持非布尔类型条件
C. 分支代码生成策略不同
D. 可以出现在类外定义
答案:A、C
解析:
题目3:泛型lambda的捕获列表中可以出现?
A. auto
类型占位符
B. 静态成员变量
C. 非静态成员函数指针
D. 模板参数包
答案:B、C
解析:
[=] { return MyClass::value; }
)[&f] { return obj.*f(); }
)auto
题目4:关于折叠表达式的错误说法是?
A. 支持所有二元操作符
B. 可以展开参数包到逗号表达式
C. 空包展开有明确定义
D. 左右折叠优先级相同
答案:A、D
解析:
||
, &&
, ,
以外的操作符需要括号包裹(args + ...)
和右折叠(... + args)
优先级不同([](auto x){}, ...)
)identity
,右折叠为identity
题目5:C++23中std::type_identity_t
的主要用途是?
A. 禁止类型推导
B. 强制类型转换
C. 消除ADL影响
D. 延迟模板实例化
答案:A、C
解析:
std::type_identity_t
阻止T
被推导(如decltype(auto) = std::type_identity_t{}
)std::type_identity_t::type
)题目6:关于模块化的正确说法是?
A. 模块接口单元必须使用export
关键字
B. 模块实现单元可以包含#include
指令
C. 模块可以导出宏定义
D. 模块编译单元之间自动处理依赖关系
答案:A、D
解析:
export module M; export int f();
)#include
题目7:C++20 requires
子句可以用于?
A. 函数模板约束
B. 类模板约束
C. 变量模板约束
D. 构造函数约束
答案:A、B、C、D
解析:
requires
可用于任何模板声明的约束(如template requires C class C {};
)题目8:关于[[nodiscard]]
属性的模板化用法正确的是?
A. 可以作用于类模板
B. 可以作用于函数模板
C. 可以作用于变量模板
D. 无法作用于成员模板
答案:A、B、C
解析:
[[nodiscard]] struct S {};
[[nodiscard]] template T f();
[[nodiscard]] template T x;
class C { [[nodiscard]] T f(); };
)题目9:C++23中std::is_constant_evaluated()
的典型应用场景是?
A. 禁止编译期计算
B. 优化运行时分支
C. 实现编译期调试输出
D. 检测异常安全等级
答案:B、C
解析:
if (!std::is_constant_evaluated()) { /* runtime code */ }
)if constexpr
)题目10:关于std::format
与模板的结合错误说法是?
A. 可以格式化任意类型(需自定义formatter
)
B. 支持编译期字符串检查
C. 自动推导格式说明符
D. 比printf
更易出错
答案:C、D
解析:
std::format("{:.2f}", 3.1415)
)printf
constexpr
上下文支持编译期检查题目1:实现一个支持编译期字符串拼接的concat
模板
#include
#include
template<std::size_t N, std::size_t M>
constexpr auto concat(const char(&a)[N], const char(&b)[M]) {
std::array<char, N + M - 1> result{};
std::copy_n(a, N-1, result.begin());
std::copy_n(b, M, result.begin() + N - 1);
return result;
}
int main() {
constexpr auto str = concat("Hello", " World!");
static_assert(str == "Hello World!");
std::cout << str.data() << std::endl;
}
题目2:使用constexpr if
优化斐波那契数列计算
#include
template<int N>
constexpr int fib() {
if constexpr (N <= 1) return N;
return fib<N-1>() + fib<N-2>();
}
int main() {
static_assert(fib<10>() == 55);
std::cout << "Fibonacci(10): " << fib<10>() << std::endl;
}
题目3:实现类型安全的变长参数包装器
#include
#include
template<typename... Args>
void log(Args&&... args) {
((std::cout << std::forward<Args>(args) << " "), ...);
std::cout << std::endl;
}
int main() {
log("Hello", 42, 3.14, std::string("World"));
}
题目4:基于std::variant
的类型安全访问器
#include
#include
#include
template<typename... Ts>
struct overloaded : Ts... { using Ts::operator()...; };
template<typename... Ts> overloaded(Ts...) -> overloaded<Ts...>;
int main() {
std::variant<int, std::string> v = "Hello";
std::visit(overloaded{
[](int i) { std::cout << "int: "<< i << std::endl; },
[](const std::string& s) { std::cout << "string: "<< s << std::endl; }
}, v);
}
题目5:编译期素数检测
#include
#include
template<unsigned N, unsigned D>
struct is_prime_helper {
static constexpr bool value = (N % D != 0) && is_prime_helper<N, D-1>::value;
};
template<unsigned N>
struct is_prime_helper<N, 1> {
static constexpr bool value = true;
};
template<unsigned N>
constexpr bool is_prime() {
if constexpr (N < 2) return false;
return is_prime_helper<N, N/2>::value;
}
int main() {
static_assert(is_prime<7>());
static_assert(!is_prime<9>());
std::cout << "Is 13 prime? " << std::boolalpha << is_prime<13>() << std::endl;
}
解析:
concept
可以定义默认约束(如template concept C = ...
)&&
, ||
, !
组合约束(如C1 && !C2
)requires
)答案:A、C
constexpr if
在编译期求值条件并丢弃不满足的分支答案:B、C
答案:A、D
答案:A、C
std::type_identity_t
阻止类型推导并消除ADL影响答案:A、D
答案:A、B、C、D
requires
可约束所有模板类型答案:A、B、C
[[nodiscard]]
可修饰类/函数/变量模板答案:B、C
std::is_constant_evaluated()
优化运行时分支和调试输出答案:C、D
std::format
需显式指定格式且比printf
更安全编译期字符串拼接
std::array
存储结果,递归拼接字符数组斐波那契数列优化
constexpr if
终止递归条件,避免无限展开变长参数包装器
类型安全访问器
std::visit
结合overloaded
实现多态访问逻辑编译期素数检测
std::bit
辅助优化