`test.cpp(47): error C2672: 'in': no matching overloaded function found`
`test.cpp(47): error C7602: 'in': the associated constraints are not satisfied`
`test.cpp(34): note: see declaration of 'in'`
随着编译器的改进,概念在出错信息上的优势在消减,但在代码表达上的优势仍然是实实在在的。记得[[第 14 讲]](https://time.geekbang.org/column/article/181636) 里我们费了好大的劲、用了几种不同的方法来定义 `has_reserve` 吗?在概念面前,那些就成了“回”字有几种写法了。我们可以飞快地定义下面的概念:
```
template <typename T>
concept has_reserve =
requires(T& dest) {
dest.reserve(1U);
};
```
这个概念用在编译期条件语句里,效果和之前的完全相同……哦,错了,不用再写 `::value` 或 `{}` 了😂。
在[[第 13 讲]](https://time.geekbang.org/column/article/181608) 我给出过的 `fmap`,在实际代码中我也是用了 SFINAE 来进行约束的(略简化):
```
template <
template <typename, typename>
class OutContainer = vector,
typename F, class R>
auto fmap(F&& f, R&& inputs)
-> decltype(
begin(inputs),
end(inputs),
OutContainer<decay_t<
decltype(f(*begin(
inputs)))>>());
```
我费了老大的劲,要把返回值写出来,实际上就是为了利用 SFINAE 而已。如果使用“概念”,那代码可以简化成:
```
template <
template <typename, typename>
class OutContainer = vector,
typename F, class R>
requires requires(R&& r) {
begin(r);
end(r);
}
auto fmap(F&& f, R&& inputs);
```
上面的 `requires requires` 不是错误,正如 `noexcept(noexcept(…))` 不是错误一样。第一个 `requires` 开始一个 **requires 子句**,后面跟一个常量表达式,结果的真假表示是否满足了模板的约束条件。第二个 `requires` 则开始了一个 **requires 表达式**:如果类型 `R` 满足约束——可以使用 `begin` 和 `end` 对 `R&&` 类型的变量进行调用——则返回真,否则返回假。
不过,在 C++20 里,上面这个条件我是不需要这么写出来的。有一个现成的概念可用,这么写就行了:
```
template <
template <typename, typename>
class OutContainer = vector,
typename F, class R>
requires range<R>
auto fmap(F&& f, R&& inputs);
```
如你所见,我今天第二次用了 `range` 这个概念。究竟什么是 range?我们留到下一讲再说。
## 内容小结
今天我们讨论了 C++20 里可以说是最重要的新功能——概念。概念可以用来对模板参数进行约束,能取代 SFINAE,产生更好、更可读的代码。
注意本讲的内容并非一个形式化的描述,请你在阅读了本讲的内容之后,再对照参考资料 [6] 的内容看一下更严格的描述,然后再回过头来读一下例子,来加深你对本讲内容的理解。
## 课后思考
请结合自己的 C++ 项目,考虑一下,“概念”可以为开发具体带来哪些好处?反过来,负面的影响又可能会是什么?
## 参考资料
[1] Bjarne Stroustrup, “Concepts: the future of generic programming, or how to design good concepts and use them well”. [http://www.stroustrup.com/good_concepts.pdf](http://www.stroustrup.com/good_concepts.pdf)
[2] Alexander Stepanov and Paul McJones, **Elements of Programming**. Addison-Wesley, 2009. 有中文版(裘宗燕译《编程原本》,人民邮电出版社,2019 年)
[3] ISO/IEC JTC1 SC22 WG21, N4549, “Programming languages — C++ extensions for concepts”. [http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4549.pdf](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4549.pdf)
[4] Herb Sutter et al., “Rename concepts to standard_case for C++20, while we still can”. [http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1754r1.pdf](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1754r1.pdf)
[5] cppreference.com, “Standard library header <concepts>”. [https://en.cppreference.com/w/cpp/header/concepts](https://en.cppreference.com/w/cpp/header/concepts).
[5a] cppreference.com, “标准库头文件 <concepts>”. [https://zh.cppreference.com/w/cpp/header/concepts](https://zh.cppreference.com/w/cpp/header/concepts).
[6] Casey Carter et al., cmcstl2. [https://github.com/CaseyCarter/cmcstl2](https://github.com/CaseyCarter/cmcstl2)
[7] cppreference.com, “Constraints and concepts”. [https://en.cppreference.com/w/cpp/language/constraints](https://en.cppreference.com/w/cpp/language/constraints)
[7a] cppreference.com, “约束与概念”. [https://zh.cppreference.com/w/cpp/language/constraints](https://zh.cppreference.com/w/cpp/language/constraints)
[8] cppreference.com, “std::enable_if”. [https://en.cppreference.com/w/cpp/types/enable_if](https://en.cppreference.com/w/cpp/types/enable_if)
[8a] cppreference.com, “std::enable_if”. [https://zh.cppreference.com/w/cpp/types/enable_if](https://zh.cppreference.com/w/cpp/types/enable_if)
[9] Andrew Sutton, “Introducing concepts”. [https://accu.org/index.php/journals/2157](https://accu.org/index.php/journals/2157)