Частичная специализация для вариационного шаблона требует сначала невариантного параметра шаблона

Следующий код

#include <iostream>
#include <utility>

template<typename F, typename... T>
struct Wrapper{ };

template<typename T>
struct is_wrapper : std::false_type {};

template<typename... T>
struct is_wrapper<Wrapper<T...>> : std::true_type {};

//template<typename F, typename... T>
//struct is_wrapper<Wrapper<F, T...>> : std::true_type {};

int main()
{
    Wrapper<int, double> w;

    std::cout << is_wrapper<decltype(w)>::value << std::endl;
}

печатает 0. Однако, если разбить две строки посередине, она печатает 1.

Почему он не всегда печатает 1? Должна ли вторая второстепенная специализация охватывать случай, который, по-видимому, покрывается третьей (прокомментированной) частичной специализацией?

Ответ 1

Код действительно должен соответствовать частичной специализации; Стандарт никогда фактически не запрещал это, но компиляторы действительно занимали некоторое время, чтобы реализовать вариативные шаблоны и их вычет должным образом. GCC соответствует с 4.9.0 и Clang по состоянию на 3.6. Соответствующий отчет об ошибке для Clang # 22191 (я не могу найти GCC, хотя).

Ответ 2

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

Ваш код удивил меня, потому что я думаю, что вы правы, полная вариационная специализация вашей черты соответствует дырочным случаям... Сначала я попытался использовать объявление вперед своего класса признаков и определить ТОЛЬКО полную вариационную специализацию (Итак, если параметр признака не является экземпляром Wrapper, компиляция не выполняется). И это именно то, что произошло, разочаровало меня снова:

#include <iostream>
#include <utility>

template<typename F , typename... T>
struct Wrapper {};

template<typename T>
struct is_wrapper;

//template<typename T>
//struct is_wrapper : std::false_type {};

template<typename... T>
struct is_wrapper<Wrapper<T...>> : std::true_type {};

//template<typename F, typename... T>
//struct is_wrapper<Wrapper<F, T...>> : std::true_type {};

using my_wrapper_type = Wrapper<int,double>;

int main()
{   
    std::cout << std::boolalpha  << is_wrapper<my_wrapper_type>::value << std::endl;
}//"Invalid use of incomplete type" ^^^^^^^^^^^^^^^^^^^^^^^^^^^

Наконец, я попробовал метод прямого объявления в классе Wrapper. И удивительно, что он работает:

#include <iostream>
#include <utility>

template<typename... T>
struct Wrapper;

template<typename F, typename... T>
struct Wrapper<F,T...>{ };


template<typename T>
struct is_wrapper : std::false_type {};

template<typename... T>
struct is_wrapper<Wrapper<T...>> : std::true_type {};

//template<typename F, typename... T>
//struct is_wrapper<Wrapper<F, T...>> : std::true_type {};


using my_wrapper_type = Wrapper<int,double>;

int main()
{
    std::cout << std::boolalpha  << is_wrapper<my_wrapper_type>::value << std::endl;
}

Отпечатки:

True

Здесь - код, запущенный на ideone.

С уважением, я не понимаю, почему ваш код выходит из строя, а мой работает. Его ошибка компилятора, или есть что-то, чего нам не хватает? Я не знаю.

Ответ 3

Если я хорошо понимаю вашу проблему, это просто приоритет специализации,

is_wrapper<decltype(w)>

Может быть специализирован по 2 шаблонам:

template<typename T> // exact specialization
template<typename... T> // variadic specialization with one parameter 

В этом случае компилятор выбирает в приоритете точную специализацию, поэтому шаблон никогда не устанавливается в вашем случае.