有时我会看到以下声明:
template<typename> // <-- not "typename T"
struct A { ... };
这种声明的用例是什么。这些有用还是只是风格问题?
有时我会看到以下声明:
template<typename> // <-- not "typename T"
struct A { ... };
这种声明的用例是什么。这些有用还是只是风格问题?
您是否真的看到了模板定义的用途,而不是模板声明(仅)?
一些用途:
// declaration only: the parameter name has no use beyond documentation
template<typename>
struct A;
// this is fine
template<typename T>
void eat_an_a(A<T> a);
// later, we can name the parameter to use it
template<typename T>
struct A { ... };
// C++0x only
template<
typename T
// We don't care for the actual type (which will default to void)
// the goal is sfinae
, typename = typename std::enable_if<
std::is_array<typename std::decay<T>::type>::value
>::value
>
void
f(T&& t);
// We still don't care to name that defaulted parameter
template<typename T, typename>
void f(T&& t)
{ ... }
Johannes 已经对您链接到的非常特殊的案例进行了解释,但显然您发现它并不令人满意。我将向您介绍这是如何工作的。让我们假设一个任意的特征类:
// no definition
template<typename TypeToExamine, typename ImplementationDetail = void>
struct trait;
我在名称中说明了类型参数的作用。现在这个声明允许的,因为第二个参数是默认的,是一些语法糖。无论trait<U>
出现在哪里,就好像我们已经写过trait<U, void>
一样。现在让我们为我们的 trait 的基本情况提供一个定义:
// assume previous declaration is still in scope so we do not default
// the second parameter again
template<typename T, typename> struct trait: std::false_type {};
这不是一个非常有用的特性。现在,当我们编写trait<U>
,它是 的缩写时trait<U, void>
,我们最终得到了这个定义。这意味着它trait<U>::value
是有效的并且实际上是false
。让我们通过添加秘密成分使我们的类更有用:
template<typename> struct void_ { typedef void type; };
// again, assume previous declarations are in scope
template<typename T, typename void_<decltype( T() + T() )>::type>
struct trait: std::true_type {};
同样,当我们写 时trait<U>
,就好像我们写了trait<U, void>
。部分专业化不会改变这一点(不允许这样做)。但是我们查询时应该使用什么定义trait<U>::value
呢?好吧,首先,我们必须知道专业化应该匹配什么;或者,神秘的第二个论点是typename void_<decltype( T() + T() )>::type
什么?
最简单的情况是when 格式U() + U()
不正确。然后 SFINAE 介入,就好像专业化不存在一样;因此我们得到了非专业定义,并且value
是false
。然而,如果U() + U()
它是合式的,则decltype
产生一个类型,并且整个变成void
,因为对于所有类型void_<T>::type
都是void
。所以这意味着我们有一个专门化的形式trait<T, void>
。这可以匹配trait<U>
,简单地T
匹配U
。而现在value
是true
。
但是,如果已经编写了专业化
template<typename T>
struct trait<T, decltype( T() + T() )>: std::true_type {};
那么它的唯一使用方式就是在写作时使用trait<U, decltype(U() + U())>
,除非 decltype(U() + U())
碰巧是无效的。记住,trait<U>
是糖trait<U, void>
。所以trait<int>
永远不会匹配我们的专业化,因为后者的形式是trait<int, int>
.
因此,如果它们没有被 SFINAE 淘汰,则void_
始终具有形式的专业化。trait<T, void>
因为我们根本不关心使用类型参数,所以它没有被命名。
这可以从类型 -> 的静态成员创建编译时映射A
。
template<typename>
struct sort_statistics { static size_t times_comparator_called = 0; };
template<typename T>
size_t sort_statistics<T>::times_comparator_called;
template<typename Titer>
void my_sort( Titer first, Titer last )
{
//...
++sort_statistics<iterator_traits<Titer>::value_type>::times_comparator_called;
if (*itA < *itB) {
//...
}
它仍然可以专业化。