Частная специализация шаблона

Может кто-нибудь объяснить, почему эти две специализации неотличимы от компилятора (gcc 4.5.1 @ideone)

http://ideone.com/9tNux

template <typename... T> struct S;

template<typename A, typename B, typename... C>
struct S<A, B, C...> {
   int f() {return 1;}
};

template<typename... A, typename... C>
struct S< S<A...>, C...> {
   int f() {return 2;}
};

и когда я пытаюсь создать экземпляр S<S<a, b>, a, b> o2;, компилятор жалуется:

prog.cpp:20:21: error: ambiguous class template instantiation for 'struct S<S<a, b>, a, b>'
prog.cpp:6:22: error: candidates are: struct S<A, B, C ...>
prog.cpp:11:33: error:                 struct S<S<A ...>, C ...>
prog.cpp:20:21: error: aggregate 'S<S<a, b>, a, b> o2' has incomplete type and cannot be defined

И когда последняя специализация изменена на:

template<typename... A, typename B, typename... C>
struct S< S<A...>, B, C...> {
   int f() {return 2;}
}

все работает нормально.

Ответ 1

Мое понимание проблемы:

typedef S<S<a, b>, c, d> S2;

Здесь S<a,b> лучше соответствует второй специализации. Однако c, d лучше подходит для остальных аргументов первой специализации (единственный список arg + list vs list). Следовательно, это 1:1.

Если вы комментируете в B во второй специализации, то вторая специализация лучше соответствует, потому что она более специализирована для первого аргумента (S<...>), а остальные одинаково хороши.

Ответ 2

Я сделал это беспорядок; теперь это должно быть хорошо, но кредит связан с @UncleBens ниже, кто правильно понял (и должен получить "accept" ).


Без B в вашей третьей версии у вас есть две частичные специализации, которые одинаково специфичны, когда вы создаете экземпляр S<S<X,Y,Z>, T1, T2, T3>:

  • Первый PS: A = S<X,Y,Z>, B = T1, C... = T2, T3.
  • Второй PS без B: A... = X,Y,Z, C... = T1, T2, T3.
  • Второй PS с B: A... = X,Y,Z, B = T1, C... = T2, T3.

Это не устанавливает сопоставимые элементы в упорядочении частичной специализации!

Обратите внимание, что вы можете сказать template <typename ...> struct S; и template <typename A, typename ...B> struct S<A, B...>;, а второй - более конкретный, чем первый, поскольку он имеет больше невариантных параметров.

Но, с другой стороны, без B, когда вы говорите S<S<X,Y,Z>,T1,T2,T3>, тогда первый аргумент лучше подходит во втором PS, но остальные аргументы лучше подходят для первого PS. Тем не менее, при использовании B второй PS более конкретный.

Сравните это с частичной специализацией, которая является более конкретной:

template <typename ...A, typename B, typename ...C>
struct S<B, std::tuple<C...>, std::tuple<C...>> { /* ... */ };

Теперь ясно, соответствует ли данный экземпляр специализации или только общей форме. Специализация имеет фиксированное количество параметров (3), поэтому выигрывает по другой специализации с переменным числом аргументов.