在本文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
是必要的。有人可以解释一下吗?
答案 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
输入。