上面例子中,栈的实现使用了std::vector。为了让栈的实现更加灵活,我们将实现栈的容器也作为模板的参数之一。
template<typename T, typename Container = std::vector<T>>
struct Stack
{
void push(const T& elem)
{
elems.push_back(elem);
}
T pop()
{
if(empty()) throw std::out_of_range("Stack<>::pop: empty!");
auto elem = elems.back();
elems.pop_back();
return elem;
}
bool empty() const
{
return elems.empty();
}
private:
Container elems;
};
如上我们为类模板增加了一个类型参数Container
。和函数类似,模板也支持声明默认参数,这里我们将Container默认设值为std::vector<T>
。由于在模板的形参声明过程中,后面的声明可以使用前面出现过的形参,我们在typename Container = std::vector<T>
中使用了T,因此这里std::vector<T>
仍旧可看做是一个具体类型。
现在我们还可以像之前一样使用该类模板,当使用Stack<int>
定义intStack
时Container使用默认的std::vector<T>
。同时我们可以显示指定Container为别的容器类,例如Stack<int, std::deque<int>> intStack;
。
每个用于替换形参Container的具体类型,必须满足Stack使用Container时的隐式约束。例如上例中Stack使用了Container的无参构造函数,调用了Container的push_back
,back
,pop_back
,empty
接口,并且对每个接口的参数和返回值都有一定预期。上面使用的std::vector<T>
和std::deque<T>
都满足如上约束。一旦不满足,会出现编译错误。
目前C++中,模板这种对参数特征的约束只能通过参数被使用的情况隐式推断出来,后续的标准后可能会引进concept的特性使程序员可以对约束显式化描述。