前言
模板技术是c++中的一项非常重要的技术。
而SFINAE更是在整个体系中起着举足轻重的分量。
而对于支撑这概念的各种技巧也是在不断发展。
Cpp20前
在cpp20前通常是用 std::enable_if<>
来实现。
比如要判断一个类型是否是const限定,可以像下面这么写。
cpp11
#include <type_traits>
template <typename T,
typename C = typename std::enable_if<std::is_const<T>::value>::type>
void fun() {
}
int main() {
// ok
fun<const int>();
// error
// fun<int>();
return 0;
}
cpp14~cpp17
在cpp14 中对enable_if 定义了辅助变量模板 enable_if_t
在cpp17 中对is_const 定义了辅助变量模板 is_const_v
#include <type_traits>
template <typename T,
typename C = typename std::enable_if_t<std::is_const_v<T>>>
void fun() {
}
int main() {
// ok
fun<const int>();
// error
// fun<int>();
return 0;
}
Cpp20
下面两种方式均等效。
concept
在cpp20中可以直接用concept 关键字定义一个约束
并在模板中直接使用整个约束
#include <iostream>
#include <concepts>
#include <type_traits>
template <class T>
concept IsConst = std::is_const_v<T>;
template <IsConst T>
void fun(T t) {
std::cout << t << std::endl;
}
int main() {
// ok
fun<const int>(1);
// error
// fun<int>();
return 0;
}
requires
#include <iostream>
#include <concepts>
#include <type_traits>
template <class T>
requires std::is_const_v<T>
void fun(T t) {
std::cout << t << std::endl;
}
int main() {
// ok
fun<const int>(1);
// error
// fun<int>();
return 0;
}
概念与约束的规则
requires 子句的使用规则
- 要求是为bool的纯右值常量表达式
- 是初等表达式 或者 带括号的表达式
- 可以使用&&或者||连接表达式 (遵循短路原则)
顺序规则
下方例子为《现代C++语言核心特性解析》中p399,400的一个例子
#include <concepts>
#include <type_traits>
template <class C>
concept ConstType = std::is_const_v<C>;
template <class C>
concept IntegralType = std::is_integral_v<C>;
template <ConstType T>
requires std::is_pointer_v<T>
void foo(IntegralType auto)
requires std::is_same_v<T, char* const>
{
}
int main() {
// 满足所有条件
foo<char *const>(1);
return 0;
}
顺序分别为出现顺序:
template <ConstType T>
requires std::is_pointer_v<T>
IntegralType auto
requires std::is_same_v<T, char* const>
requires 表达式
是的将 concept 和 requires 结合还能写更像函数。
注意在scope中可以写多行,是且的条件。
#include <concepts>
#include <type_traits>
template <class T>
concept check = requires {
// 满足有该表达式的才允许通过
// 只会在编译阶段判断合法性,而不会实际运行
T{}.resize(1);
};
template <class C>
concept IntegralType = std::is_integral_v<C>;
template <check T>
void foo(T) {
}
#include <queue>
#include <string>
#include <vector>
int main() {
// ok
foo<std::vector<int>>({});
foo(std::string{});
// error
// foo(std::queue<int>{});
return 0;
}