Каковы случаи, когда вы не объявляете имя для typename в шаблоне? - PullRequest
7 голосов
/ 03 июля 2011

Иногда я вижу ниже вид объявления:

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

Каковы варианты использования для такого объявления. Они полезны или просто вопрос стиля?

Ответы [ 2 ]

5 голосов
/ 03 июля 2011

Вы действительно видели, что используется для определения шаблона, а не, скажем, для объявления шаблона (только)?

Некоторые виды использования:

// 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. Поскольку мы просто не хотим использовать параметр типа, он не называется.

1 голос
/ 03 июля 2011
  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. Это все еще может быть специализированным.

...