1

在这篇文章Traits:一种新的有用的模板技术中,traits 首次在 C++ 中介绍了traits,作者强调了以下几点:

template <class numT>
class matrix {
public:
  typedef numT num_type;
  typedef float_traits<num_type> traits_type;
  inline num_type epsilon() { return traits_type::epsilon(); }
  ...
};

请注意,在迄今为止的所有示例中,每个模板都提供typedef了其参数的 public s,以及依赖于它们的任何东西。这绝非偶然:在多种情况下,用于实例化模板的参数不可用,只有typedef在模板声明中以 s 形式提供时才能检索。道德:总是提供这些typedefs

但让我感到困惑的是,没有任何情况表明typedef需要使用 。谁能解释一下?

4

2 回答 2

4

假设您有一个接受以下通用容器的函数模板T

template <typename ContainerT>
void DoThings(ContainerT const& input)
{
   // Do something
}

在此方法中,您想要获取容器中某物的迭代器。但是你不知道迭代器的类型;你只得到了容器的类型,而不是迭代器:

template <typename ContainerT>
void DoThings(ContainerT const& input)
{
   /* ??? */ it = input.begin();
}

假设为了论证您可以解决该问题,并且您想取消对迭代器的引用。要存储结果,您需要知道是什么T;但这又会导致同样的问题:

template <typename ContainerT>
void DoThings(ContainerT const& input)
{
   /* ??? */ it = input.begin();
   /* ??? */ firstElement = *it;
}

我们不知道T,容器中包含的类型是什么;我们在这个函数模板中只有容器本身的类型。为了获得容器内的类型,我们需要容器来帮助我们一点,告诉我们它包含的类型是什么。容器类通过typedefs 做到这一点。对于标准容器,这将iterator用于迭代器类型和value_type包含的值类型:

template <typename ContainerT>
void DoThings(ContainerT const& input)
{
   // Container provides its iterator type via a typedef.
   typename ContainerT::iterator it = input.begin();

   // Container provides its contained type via a typedef.
   typename ContainerT::value_type firstElement = *it;
}

即使在 C++11 中,您也可以使用以下方法解决上述示例auto

template <typename ContainerT>
void DoThings(ContainerT const& input)
{
   // Make the compiler figure it out:
   auto it = input.begin();
   auto firstElement = *it;
}

有时您仍然希望能够为其他目的获取实际类型:

// Failure to compile: http://ideone.com/vxJ2IU
// Successful compile: http://ideone.com/b5fU3S
#include <vector>
#include <list>
#include <deque>
#include <type_traits>
#include <iostream>

template <typename ContainerT>
void DoThings(ContainerT const& input)
{
   // DoThings only accepts integral containers:
   static_assert(
      std::is_integral<typename ContainerT::value_type>::value,
      "DoThings requires that the contained type be integral");
   // Make the compiler figure it out:
   auto it = input.begin();
   auto firstElement = *it;
   std::cout << firstElement;
}

int main()
{
    std::vector<int> abc;
    abc.push_back(42);

    std::list<long> def;
    def.push_back(1729);

    std::deque<short> queue;
    queue.push_back(1234);

    DoThings(abc);
    DoThings(def);
    DoThings(queue);

    // Does not compile due to static assert:
    std::vector<double> doubles;
    doubles.push_back(3.14);
    DoThings(doubles);
}
于 2013-04-19T03:39:38.093 回答
1

一个简单的例子:


假设您想编写一个通用的sum函数,将STL container. 这可以vectorlistset。并且内容可能是int's、float's 或string's(对于字符串 sum 将是串联)(为了简单起见)。

如果容器包含int's 那么总和将是int。如果它包含floats,那么总和将不是一个点,int而是一个floating点。因为strings它应该是一个string. 其他一切都是一样的(函数内部的操作。


写这个的一种方法是

template<typename T>
T sum(const vector<T>& t) {
   T total = T();
   // iterate and sum.
   return total;
}

但问题是现在您需要为每种容器类型(set, list,..)编写此函数。所以它并不是那么通用。


为了更通用,您需要编写类似这样的内容

template<typename T>
?? sum(const T& t) {
   ?? total;
   // iterate and sum.
   return ??
}

但是在这里返回什么?您将如何知道容器中包含的内容。这就是typedefs进来的地方。幸运的 STL 容器有几个 typedef,可以让您了解它们的作用和能力。对于我们的案例,他们将包含的类型定义为value_type我相信您C::iterator在某些时候使用过它也是 typedef)。


现在我们的小和函数可以写成

template<typename T>
typename
T::value_type sum(const T& t) {
   typedef typename T::value_type v_type;;
   v_type total = v_type();
   // iterate and sum.
   return total;
}

通常,使用typdefs. 例如,如果您正在设计一个C具有两种模板类型的模板类,T并且V

template<typename T, typename V>
class C {
    typedef T t_type;
    typedef V v_type;
    //
    //
}

这对于使用C. 他们可以很容易地找到类型T和类型V的对象c是否C被键入。

于 2013-04-19T04:17:11.657 回答