但是约束类最多是一个不完整的解决方案。例如,在定义中进行测试--如果检查工作只能在声明中完成,那么就会好很多。使用这种方式的时候,我们必须遵循接口的使用规则,并且可以开始考虑真正的模板分开编译的可能性问题。
因此,让我们告诉编译器我们所期望的模板参数:
template
In find_if(In first, In last, Pred pred);
假设我们能够表示出Forward_iterator和Predicate是什么,那么编译器现在可以不理会它的定义,单独地检查find_if()调用了。这时我们所需要做的工作是为模板参数建立一个类型 系统。在现代C++环境中,这种"类型的类型(types of types)"被称为"概念(concepts)"。我们可以通过很多途径来说明这种概念;从现在开始,把它们想作是直接受到语言支持的、拥有更好的语法的约束类。一个概念说明了某种类型必须提供的什么工具,而不是说明它如何提供这些工具。完美的概念(例如
只要给出了find_if()的这种声明(并且不是定义)之后,我们就可以编写
int x = find_if(1,2,Less_than(7));
这个调用会失败,因为int不支持*。换句话说,这个调用在编译时会失败,因为int不是一个Forward_iterator。重要的是,它使得编译器容易报告用户语言中的错误,并且在编译时,调用会被首先看到。
不幸的是,知道迭代子参数是Forward_iterator并且谓词参数是Predicate也不足以保证find_if()调用成功编译。这两个参数是互相影响的。特别是谓词的参数是一个使用*(pred(*first))解除引用的迭代子。我们的目的是在与调用分离的情况下,完善模板的检测,同时在不查看模板定义的情况下,完善每个调用的检查,因此概念必须有充分的表现能力,能够处理模板参数之中的这类迭代子。一种办法是用平行的参数来表示概念,这与模板的参数化方式类似。例如:
template Forward_iterator In, // 迭代子在T序列中
Predicate Pred> // 带有 T 参数并返回一个布尔值
In find_i