8

有时我会看到以下声明:

template<typename>  // <-- not "typename T"
struct A { ... };

这种声明的用例是什么。这些有用还是只是风格问题?

4

2 回答 2

7

您是否真的看到了模板定义的用途,而不是模板声明(仅)?

一些用途:

// 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 介入,就好像专业化不存在一样;因此我们得到了非专业定义,并且valuefalse。然而,如果U() + U()它是合式的,则decltype产生一个类型,并且整个变成void,因为对于所有类型void_<T>::type都是void。所以这意味着我们有一个专门化的形式trait<T, void>。这可以匹配trait<U>,简单地T匹配U。而现在valuetrue

但是,如果已经编写了专业化

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>因为我们根本不关心使用类型参数,所以它没有被命名。

于 2011-07-03T05:32:15.773 回答
1
  1. 这可以从类型 -> 的静态成员创建编译时映射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) { 
        //...
    }
    
  2. 它仍然可以专业化。

于 2011-07-03T05:24:24.847 回答