Частичная специализация шаблона класса: ошибка компилятора - PullRequest
1 голос
/ 01 октября 2019

Эта программа

#include <iostream>

template <int I>
struct A
{
    A() { std::cout << "A<I>()\n"; }
};

template <int I>
struct A<I + 5>
{
    A() { std::cout << "A<I + 5>()\n"; }
};


int main()
{
    return 0;
}

не компилируется ни gcc HEAD 10.0.0 20190, ни clang HEAD 10.0.0.

Например, компилятор gcc выдает ошибку

prog.cc:10:8: error: template argument '(I + 5)' involves template parameter(s)
   10 | struct A<I + 5>
      |        ^~~~~~~~

Есть ли неправильная частичная специализация шаблона класса?

Ответы [ 2 ]

6 голосов
/ 01 октября 2019

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

[temp.class.spec]

8 в списке аргументовчастичной специализации шаблона класса применяются следующие ограничения:

  • Специализация должна быть более специализированной, чем основной шаблон.

"Специализация не болееспециализированный !?»Я полагаю, вы думаете. Но это действительно так. Правила определения того, что является «более специализированным», описаны в [temp.class.order] . Суть в том, что мы должны рассмотреть две гипотетические перегрузки шаблонов функций:

template <int I>
struct A { /* ... */ };

template<int I>
void foo(A<I>); //1

template<int I>
void foo(A<I + 5>); //2

Затем мы выполняем разрешение перегрузки и частичное упорядочение шаблонов функций. Если победит # 2, это более специализировано, и ваша декларация законна. Если это не победит, объявление недействительно. Частичное упорядочение выполняется путем составления некоторых аргументов и вычитания аргументов шаблона одного шаблона относительно другого. Итак, предположим, что мы начнем со сравнения первого со вторым (я переименую их для простоты, но они все еще перегружены):

void foo1(A<0>); -> void foo2(A<I + 5>);

Успешен ли здесь вывод аргумента? Это не так. I + 5 - это не выводимый контекст:

[temp.deduct.type]

Неведуемые контексты:

5.3 - нетипизированный аргумент шаблона или граница массива, в которой подвыражение ссылается на параметр шаблона.

I ссылается на параметр шаблона, поэтому I + 5это не выводимый контекст. Поэтому вывод аргументов шаблона не выполняется в этом направлении.

Давайте попробуем другое направление. Снова мы выдвигаем аргумент

void foo2(A<1 + 5>); = void foo2(A<6>);  -> void foo1(A<I>); 

Удержание, очевидно, здесь успешно. Таким образом, согласно правилам частичного упорядочения шаблона функции, foo1 более специализирован, чем foo2. Это означает, что наша основная специализация действительно более специализирована, чем наша «частичная специализация», что делает частичную специализацию плохо сформированной.

1 голос
/ 01 октября 2019

Рассматривая temp.class.spec.match / 3 , мы имеем

Если аргументы шаблона частичной специализации не могут быть выведены из-за структуры его template-parameter-list и template-id , программа некорректно сформирована.

на примере

template <int I, int J> struct A {};
template <int I> struct A<I+5, I*2> {};     // error

template <int I> struct A<I, I> {};         // OK

template <int I, int J, int K> struct B {};
template <int I> struct B<I, I*2, 2> {};    // OK

Сообщение об ошибке Clang повторяет это:

error: class template partial specialization contains a template parameter that cannot be deduced; this partial specialization will never be used

(См. Ответ @ StoryTeller о том, почему в вашем коде произошел сбой, я не буду повторять это здесь.)

...