一 概述
concept是C++20 引入的语言特性,concept 是一种用于约束模板参数的编译期布尔表达式(谓词),它描述了一个类型应该具有的某些性质(比如是否可比较、是否可拷贝、是否支持某种操作等)。
在 C++20 之前,模板参数不受限制,任何类型都可以传进去,直到你在模板内部调用了不存在的函数或操作才爆炸式报错,比如这样:
template<typename T>
void print_size(const T& obj) {std::cout << obj.size() << '\n'; // 如果 T 没有 size(),编译器报个几十行的模板错误
}
报错晦涩难懂,用户根本不知道为什么错,引入 Concept 之后,我们可以提前限制参数类型,并提供更清晰的错误信息:
#include <concepts>template<typename T>
requires requires (T t) { t.size(); }
void print_size(const T& obj) {std::cout << obj.size() << '\n';
}
二:预定义concept和自定义concept
1. C++20 提供了一组标准 concept:
2. 自定义concept
假设定义一个 concept 要求类型 T
有一个 size()
成员函数,且能返回可以转换为 std::size_t
的值。
template<typename T>
concept HasSize = requires(T t) {{ t.size() } -> std::convertible_to<std::size_t>;
};
我们可以这样使用它:
template<HasSize T>
void print_size(const T& t) {std::cout << t.size() << '\n';
}
template<typename T>
requires HasSize<T>
void print_size(const T& t) {std::cout << t.size() << '\n';
}
三:concept的例子(代替SFINAE)
SFINAE
(Substitution Failure Is Not An Error)是模板编程中一个重要的机制,当模板在替换(推导)类型时,如果出现非法代码,不会报错,而是自动忽略该模板实例化版本,去尝试别的重载或模板。
下面举一个例子,写一个函数 increment()
,只允许传入可以加一的类型(比如整数),不允许传入不支持 ++
的类型(比如 std::string
)。看下C++98/11/14/20 如何实现
1. C++98 的实现(用SFINAE)
template<bool B, typename T = void>
struct enable_if {};template<typename T>
struct enable_if<true, T> {typedef T type;
};template<typename T>
struct is_integral {static const bool value = false;
};template<>
struct is_integral<int> {static const bool value = true;
};#include <iostream>template<typename T>
typename enable_if<is_integral<T>::value, T>::type
increment(T value) {return value + 1;
}
2. C++11/14 的实现(用SFINAE)
#include <type_traits>
#include <iostream>template<typename T>
typename std::enable_if<std::is_integral<T>::value, T>::type
increment(T value) {return value + 1;
}
3. C++20的实现 (用concept)
#include <concepts>
#include <iostream>template<std::integral T>
T increment(T value) {return value + 1;
}