不在模板中声明类型名称的用例有哪些?

时间:2011-07-03 05:15:13

标签: c++ templates coding-style typename

有时我会在下面看到一种声明:

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

此类声明的用例是什么?那些有用还是只是风格问题?

2 个答案:

答案 0 :(得分:5)

您是否真的看到用于模板定义,而不是仅用于模板声明?

一些用途:

// 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)
{ ... }

约翰内斯给出了你所联系到的特殊情况的解释,但显然你发现它并不令人满意。我将带你了解它的工作原理。让我们假设一个任意的特质类:

// no definition
template<typename TypeToExamine, typename ImplementationDetail = void>
struct trait;

我在名字中拼写出类型参数的作用。现在这个声明允许什么,因为第二个参数是默认的,有点语法糖。无论trait<U>出现在哪里,它都完全,好像我们已经写过trait<U, void>一样。现在让我们为我们的特征的基本情况提供一个定义:

// 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开始了,就好像专业化不存在一样;因此我们得到非专业定义,valuefalse。但是,如果U() + U()格式正确,则decltype会生成一个类型,整体会变为void,因为所有类型void_<T>::type都是void。所以这意味着我们对trait<T, void>形式进行了专门化。这可以匹配trait<U>,只需将TU匹配即可。现在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>

因此,如果void_不是SFINAE,trait<T, void>扮演的角色始终具有{{1}}形式的特化。因为我们根本不关心使用type参数,所以它没有命名。

答案 1 :(得分:1)

  1. 这可以从type - &gt;创建编译时映射。 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. 它仍然可以专业化。