Пакет параметров должен быть в конце списка параметров... Когда и почему?

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

Другими словами, этот компилируется:

class C {
    template<typename T, typename... Args, typename S>
    void fn() { }
};

Следующий:

template<typename T, typename... Args, typename S>
class C { };

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

Чтобы быть ясным, реальная проблема заключается в том, что я определял класс, аналогичный следующему:

template<typename T, typename... Args, typename Allocator>
class C { };

Если тип распределителя как последний тип будет оценен, но я могу как-то обойти его (во всяком случае, если у вас есть предложение, которое оно ценит, возможно, ваше гораздо более элегантное, чем мое!).
Тем не менее, я получил сообщение об ошибке:

пакет параметров "Args" должен быть в конце списка параметров шаблона

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

Здесь есть аналогичный вопрос, но он просто объясняет, как решить проблему, и это было мне совершенно понятно.

Ответ 1

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

template<typename T, typename... Args, typename S> void fn() { }
int main() { fn<int, int, int>(); }
test.cpp: In function 'int main()':
test.cpp:2:32: error: no matching function for call to 'fn()'
 int main() { fn<int, int, int>(); }
                                ^
test.cpp:1:57: note: candidate: template<class T, class ... Args, class S> void fn()
 template<typename T, typename... Args, typename S> void fn() { }
                                                         ^
test.cpp:1:57: note:   template argument deduction/substitution failed:
test.cpp:2:32: note:   couldn't deduce template parameter 'S'
 int main() { fn<int, int, int>(); }

у компилятора нет способа определить, какие параметры шаблона относятся к пакету параметров, а к S. Фактически, как @T.C. указывает, что на самом деле это должна быть синтаксическая ошибка, потому что шаблон функции, определенный таким образом, никогда не может быть создан.

Более полезный шаблон функции будет похож на

template<typename T, typename... Args, typename S> void fn(S s) { }

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

Ничего из этого не работает для шаблонов (первичных) классов, параметры не выводятся и явно запрещены:

Из черновика n4567

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4567.pdf

[temp.param]/11

[...] Если шаблон-шаблон шаблона первичного класса или псевдонима template является пакетом параметров шаблона, он должен быть последним шаблоны параметры. [...]

(если бы они были выведены, это было бы неоднозначно, как в примере шаблона функции).

Ответ 2

Первый неправильный. Компилятор просто глючит и не смог его диагностировать. [temp.param]/11:

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


Если тип функции T(Args...) имеет смысл для конечного пользователя, одним из способов исправить это будет использование частичной специализации:

template<class F, class Alloc> class C; //undefined
template<class T, class... Args, class Alloc>
class C<T(Args...), Alloc> {
    // implementation
};

В зависимости от реальных требований можно также рассмотреть вопрос о стирании распределителя.