Специализация параметра шаблона вариационного шаблона на минимальном количестве аргументов: законный или нет?

У меня есть код:

#include <cstdio>

template<template<typename...> class>
struct Foo 
{ 
    enum { n = 77 };
};

template<template<typename, typename...> class C>
struct Foo<C>
{
    enum { n = 99 }; 
};

template<typename...> struct A { };

template<typename, typename...> struct B { };

int main(int, char**)
{
    printf("%d\n", Foo<A>::n);
    printf("%d\n", Foo<B>::n);
}

Идея состоит в том, что template<typename, typename...> class является подмножеством template<typename...> class, поэтому на нем можно было бы специализироваться. Но это довольно эзотерично, так что, возможно, нет. Попробуйте это.

GCC 4.7 говорит:

$ g++ -std=c++11 test157.cpp 

Скомпилировано!

Запуск:

$ ./a.out 
77
99

Это работает!

Clang 3.1 говорит:

$ clang++ -std=c++11 test157.cpp
test157.cpp:10:8: error: class template partial specialization does not specialize any template argument; to define the primary template, remove the template argument list
struct Foo<C>
       ^  ~~~
test157.cpp:9:10: error: too many template parameters in template template parameter redeclaration
template<template<typename, typename...> class C>
         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test157.cpp:3:10: note: previous template template parameter is here
template<template<typename...> class>
         ^~~~~~~~~~~~~~~~~~~~~
2 errors generated.

Кто прав?

Ответы

Ответ 1

Клэнг ошибочно отвергает частичную специализацию. Чтобы знать, как интерпретировать ошибку, вам нужно понять, что диалоги клана. Это означает диагностировать частичную специализацию, аргументы которой соответствуют именно списку неявных аргументов шаблона первичного класса (<param1, param2, ... , paramN>).

Однако списки аргументов по-разному, поэтому clang не должен его диагностировать. В частности, это не имеет никакого отношения к тому, что частичная специализация соответствует более или менее аргументам. Рассмотрим

template<typename A, typename B> class C;
template<typename B, typename A> class C<A, B> {};

Частичная специализация здесь соответствует всем и не более того, что соответствует первичному шаблону. И списки аргументов обоих шаблонов различны, поэтому эта частичная специализация действительна, как и вы.

Ответ 2

`template<template<typename, typename...> class C> 

не более специализирован, чем

template<template<typename...> class>

Оба из них берут список неизвестных параметров типа. это просто, что первый берет один из членов этого списка в качестве другого параметра. Он не содержит дополнительной информации о типе, так что компилятор должен выбрать один из них.

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

Clang очень увлекается диагностикой, поэтому я думаю, что он улавливает ненормальность и по праву дает ошибки. GCC, способный скомпилировать его, странно. Возможно, потому, что вы явно указываете, какие шаблоны использовать в struct A и struct B отдельно, gcc смог поймать это и подавить ненормальность.