在什么情况下用于实例化模板的参数不可用?

时间:2013-04-19 03:08:53

标签: c++ templates traits

在本文Traits: a new and useful template technique中,第一次在C ++中引入了特征,作者强调了以下内容:

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,以及依赖于它们的任何内容。 这并非偶然:在各种情况下,用于实例化模板的参数不可用,只有在模板声明中以typedef提供时才能检索。道德:总是提供这些typedefs

但让我感到困惑的是,没有任何情况表明使用typedef是必要的。有人可以解释一下吗?

2 个答案:

答案 0 :(得分: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是什么;我们在这个函数模板中只有容器本身的类型。为了获得容器内的类型,我们需要容器帮助我们一点点,并告诉我们它包含的类型是什么。容器类通过typedef来完成。对于标准容器,迭代器类型为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);
}

答案 1 :(得分:1)

一个简单的例子:


考虑您想要编写一个通用 总和 函数,该函数对 STL container 。这可以是 vector list set 。内容可能是 int float string for strings sum将是连接)(为了简单起见)。

如果容器包含 int ,则总和将为 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 的用武之地。 Lucky 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 的对象em> C 输入。