在 C++20 之前。
我们也会“限制模板”。
只是手段很笨。
你写一堆 type_traits。
再配一个 enable_if。
能跑。
但读起来像密码本。
当年:我们是怎么把类型“关在门外”的
你写个模板函数。
然后你不希望它接收所有类型。
你会这么写。
#include <type_traits>
template <class T>
std::enable_if_t<std::is_integral_v<T>, T>
inc(T x) {
return x + 1;
}
老实说。
它没错。
但它有一种“我在跟编译器讨价还价”的味道。
你不是在表达意图。
你是在绕规则。
线上啪一下:有人把函数当成参数传了个整数
你写了个小工具。
想让用户传一个回调。
你只是想“调用两次”。
template <class F>
void run_twice(F f) {
f();
f();
}
int main() {
run_twice(42);
}
这段代码的问题。
一眼就能看出来。
42 不是函数。
可编译器报错。
经常会把你带进模板深处。
让你在“可调用对象”“函数对象”“重载解析”里迷路。
C++20 的解法:直接说人话
标准库给了很多现成的 concept。
你不用自己造。
先把常用的那几个拿来当门禁。
这件事就会变得很干净。
#include <concepts>
void run_twice(std::invocable auto f) {
f();
f();
}
现在如果有人传 42。
它会更像。
“你这个参数不满足 invocable”。
而不是。
“在某个深处的实例化里调用失败”。
std::integral:把“整数”变成一个约束
很多时候你写模板。
只是想要一个整数。
而不是想要“任何能加 1 的东西”。
#include <concepts>
std::size_t round_up(std::integral auto n) {
return (n + 7) / 8 * 8;
}
这段约束的意思很朴素。
n 必须是整数类型。
你不需要解释 enable_if。
读者也不会被 std::is_integral_v 绊住。
你会常用的几个标准库 Concepts
先记住这几个。
就够你覆盖很多“模板误用”。
#include <concepts>
static_assert(std::integral<int>);
static_assert(std::floating_point<double>);
static_assert(std::convertible_to<int, double>);
它们都是“编译期规则”。
不是运行时 if。
洞见:标准库 Concepts 更像“行业术语”
你看 std::integral。
它不是在炫技。
它是在建立共同语言。
当你写下它。
团队里的人会自然理解。
“这里要整数。”
而不是。
“这里有一段奇怪的 enable_if。”
小结
自定义 concept 很爽。
但你先别急。
先把标准库那几个用起来。
你会发现。
模板错误会安静很多。
代码也更像在讲人话。