【发布时间】:2011-07-03 05:15:12
【问题描述】:
有时我会看到以下声明:
template<typename> // <-- not "typename T"
struct A { ... };
这种声明的用例是什么。这些有用还是只是风格问题?
【问题讨论】:
标签: c++ templates coding-style typename
有时我会看到以下声明:
template<typename> // <-- not "typename T"
struct A { ... };
这种声明的用例是什么。这些有用还是只是风格问题?
【问题讨论】:
标签: c++ templates coding-style typename
您是否真的看到了模板定义的用途,而不是模板声明(仅)?
一些用途:
// 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是什么?
最简单的情况是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>。
因此,void_ 扮演的角色是始终具有 trait<T, void> 形式的专业化,如果它们没有被 SFINAE 淘汰的话。由于我们根本不关心使用类型参数,所以它没有命名。
【讨论】:
class 正文。见这里:stackoverflow.com/questions/6540948/…
template<typename> struct A ...。你看到了template<typename = void> struct A ...。这非常不同,部分描述在链接内的 cmets 中,您可以在我的回答中看到匿名默认参数的另一种用法。
typename = void 无关。见答案:template<typename> struct void_ { typedef void type; };
这可以从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) {
//...
}
它仍然可以被特化。
【讨论】: