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

iam*_*ind 7 c++ templates coding-style typename

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

template<typename>  // <-- not "typename T"
struct A { ... };
Run Code Online (Sandbox Code Playgroud)

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

Luc*_*ton 6

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

一些用途:

// 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)
{ ... }
Run Code Online (Sandbox Code Playgroud)

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

// no definition
template<typename TypeToExamine, typename ImplementationDetail = void>
struct trait;
Run Code Online (Sandbox Code Playgroud)

我在名字中拼写出类型参数的作用.现在这个声明允许什么,因为第二个参数是默认的,有点语法糖.无论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 {};
Run Code Online (Sandbox Code Playgroud)

这不是一个非常有用的特质.现在,当我们写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 {};
Run Code Online (Sandbox Code Playgroud)

再一次,当我们写作时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 {};
Run Code Online (Sandbox Code Playgroud)

那么它的唯一使用方式就是写作时trait<U, decltype(U() + U())>,除非 decltype(U() + U())发生错误.记住,trait<U>是糖trait<U, void>.所以trait<int>永远不会匹配我们的专业化,因为后者是形式trait<int, int>.

因此void_,trait<T, void>如果它们不是SFINAE,那么它总是具有形式的特化.因为我们根本不关心使用type参数,所以它没有命名.