Вы действительно видели, что используется для определения шаблона, а не, скажем, для объявления шаблона (только)?
Некоторые виды использования:
// 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()
плохо сформирован. Затем начинается СФИНА, и специализация как будто не существует; таким образом, мы получаем неспецифическое определение, и 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. Поскольку мы просто не хотим использовать параметр типа, он не называется.