C++ 模板模板(双模板?)

Posted

技术标签:

【中文标题】C++ 模板模板(双模板?)【英文标题】:C++ templates template (double template?) 【发布时间】:2011-06-25 11:13:39 【问题描述】:

我想构建一个Stack 类,以便用户能够选择他想使用哪个容器来实现Stack。例如,List/Vector

部分代码:

stack.h

#ifndef STACK_H_
#define STACK_H_

template <typename T, template<typename T> class ContainerType>
class Stack
    ContainerType<T> container;
public:
    Stack() : container(ContainerType<T>())

;

#endif /* STACK_H_ */

test.cpp

#include "stack.h"
#include <vector>

int main()   
    Stack<int, std::vector<int> > stack;
    return 0;

好吧,它不编译。我在线收到下一个错误:

Stack<int, std::vector<int> > stack;

错误:

expected a class template, got `std::vector<int, std::allocator<int> >' test.cpp

invalid type in declaration before ';' token test.cpp

type/value mismatch at argument 2 in template parameter 
list for `template<class T, template<class T> class ContainerType> 
class Stack' test.cpp

‪

【问题讨论】:

这个错误是由于总是写using namespace std;并习惯它的蹩脚做法的结果。 @Armen 但真正的错误在其他地方。 【参考方案1】:

首先,它将是std::vector,仅此而已,因为vector 位于std 命名空间中,并且您要求模板模板参数,而std::vector&lt;int&gt; 不是模板了。接下来,std::vector 实际上采用 两个 模板参数,一个用于类型,另一个用于分配器:

template <
    typename T,
    template<typename, typename> class ContainerType,
    typename Alloc = std::allocator<T>
>
class Stack
  ContainerType<T, Alloc> container;
  // ...
;

// usage:
Stack<int, std::vector> s;

现在,这仅允许具有两个模板参数的容器作为底层类型,因此您最好使用标准所做的:将其作为普通类型:

template <typename T, typename ContainerType>
class Stack
  ContainerType container;
  // ...
;

// usage:
Stack<int, std::vector<int> > s;

为确保底层类型具有相同的T,您可以做一个假的“静态断言”,或者如果您有启用 C++0x 的编译器,您可以做一个实际的静态断言:

#include <tr1/type_traits> // C++03 us std::tr1::is_same
//#include <type_traits> // C++0x, use std::is_same

template <typename T, typename ContainerType>
class Stack
  typedef typename ContainerType::value_type underlying_value_type;
  typedef char ERROR_different_value_type[
               std::tr1::is_same<T, underlying_value_type>::value ? 1 : -1
                                         ]
  ContainerType container;
  // ...
;

这是因为如果T 与使用的容器的T 不同,它将是typedef char ERROR_different_vale_type[-1],并且不可能存在负大小的数组,这会导致编译器错误。 :) 现在,使用 C++0x,您只需 static_assert 即可:

#include <tr1/type_traits> // C++03
//#include <type_traits> // C++0x

template <typename T, typename ContainerType>
class Stack
  typedef typename ContainerType::value_type underlying_value_type;
  static_assert(std::tr1::is_same<T, underlying_value_type>::value,
    "Error: The type of the stack must be the same as the type of the container");
  ContainerType container;
  // ...
;

为方便起见,您现在可以为常见情况指定默认模板参数:

template <typename T, typename ContainerType = std::vector<T>>
class Stack
  ContainerType container;
  // ...
;

// usage:
Stack<int> s;

此时您可以使用std::stack,它正是这样做的(尽管它使用std::deque 作为底层类型)。 :)

【讨论】:

是的,但我也希望能够编写 Stack > 堆栈,而不仅仅是向量。如果我使用你的第二个代码块,如果我使用会发生什么: Stack > stack.它会只是一个双向量吗?我可以做一个约束,使向量的类型为 int 而不是 double(我的意思是它会像第一种类型)? @user550413:如果你写Stack &lt;int, List&gt;,那么容器类型将是List&lt;int&gt;,就像Stack &lt;int, vector&gt;得到vector时一样。 非常感谢。我在定义复制构造函数时遇到问题? @user:到底是什么?如果它与这个问题无关,请打开一个新问题! :) 可变参数模板有助于解决模板数量问题:en.wikipedia.org/wiki/Variadic_template【参考方案2】:

最简单的方法是不使用模板模板参数,因为容器的数量问题。

相反,只需传递完整的容器类型,仅此而已。然后提取value_type(标准STL内部typedef)得到值。

template <typename Container>
class Stack

public:
  typedef typename Container::value_type value_type;

private:
  Container _container;
; // class Stack<Container>

然后,您可以简单地将其用作Stack&lt; std::vector&lt;int&gt; &gt;,它将包含ints。

【讨论】:

【参考方案3】:

由于vector 属于std 命名空间,您必须对其进行限定。但除此之外,ContainerType 是模板模板参数,您需要传递模板而不是最终类型:

Stack<int, std::vector > stack;

【讨论】:

【参考方案4】:

这一行:

     Stack<int, vector<int> > stack;

应该是:

  Stack<int, std::vector<int> > stack;

或者你可以在 test.cpp 前面加上

 using namespace std;

【讨论】:

当他只需要using std::vector时,为什么要引入整个命名空间std? @user550413 看看其他答案。

以上是关于C++ 模板模板(双模板?)的主要内容,如果未能解决你的问题,请参考以下文章

c++模板例题

模板类的 C++ 强制转换

单类型和双类型变量如何在 Matlab 中的同一代码副本中工作,就像 C++ 中的模板一样

在 C++ 中,如何将返回值模板化为与参数值不同?

使用 SWIG 绑定 Python/C++ 模板

C++入门C++ 函数模板&类模板