Существует несколько разных проблем с примером кода.
1) GCC не может реализовать CWG 727 (требуется в C ++ 17): https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85282, что вызывает error: explicit specialization in non-namespace scope 'class Test'
2) Если мы проигнорируем это, пример кода можно упростить до
template<int N> auto foo() {}
template<> auto foo<0>() { return 7; }
template<int N> void bar() {}
template<> int bar<0>() { return 7; }
, который все еще показывает те же ошибки.Теперь все компиляторы согласны с выводом.Они компилируют foo
s и выдают ошибку по специализации bar
.
, почему допускается использование другого типа возврата, когда auto используется для специализации
Это разрешеностандарт для специализации функций с auto
типом возврата, если у специализаций также есть auto
заполнитель
Переопределения или специализации функции или шаблона функции с объявленным типом возврата, который использует тип заполнителя, также должениспользуйте этот заполнитель, а не выводимый тип
http://eel.is/c++draft/dcl.spec.auto#11
Таким образом, в силу этой специализации, соответствующей стандарту (аналогичной одному из приведенных примеров), и не являющейся явнозапрещено везде, где это разрешено.
Что касается ошибки с bar
, стандарт гласит, что тип возвращаемого значения является частью сигнатуры шаблона функции:
«шаблон функции» имя, параметрсписок типов ([dcl.fct]), включающий пространство имен (если есть), тип возвращаемого значения , заголовок шаблона и конечный запрос require-clause ([dcl.decl]) (если есть)
http://eel.is/c++draft/defns.signature.templ
Таким образом, в глазах компилятора template<> int bar<0>() { return 7; }
является специализацией шаблона template<... N> int bar();
(обратите внимание натип возврата).Но это не было объявлено ранее (специализация не может предшествовать объявлению), поэтому компиляция не удалась!Если вы добавите template<int N> int bar();
, то он скомпилируется (но вы будете жаловаться на неоднозначность вызовов, если попытаетесь вызвать bar
).
В принципе, вы не можете изменить сигнатуру функции в специализации, вы можете толькопараметры шаблона specialize (duh) (которые также должны быть такими же, как в объявлении, поскольку они также являются частью подписи).
Могу ли я иметь специализацию шаблона с явно объявленным типом возврата, отличным отбазовый шаблон вообще
Как объяснено - вы не можете изменить сигнатуру шаблона функции, что означает, что вы не можете изменить тип возвращаемого значения.НО, вы можете специализировать тип возвращаемого значения, если он зависит от параметра шаблона!
Учитывайте
template<int N, typename R = void> R bar() {}
template<> int bar<0>() { return 7; }
// bar<0, int> is deduced, see: http://eel.is/c++draft/temp.expl.spec#10
Это разрешено, но имеет недостаток, заключающийся в необходимости писать bar<0, int>
, когда вы хотитечтобы вызвать специализацию: https://godbolt.org/z/4lrL62
Эту проблему можно обойти, сделав тип условным в исходном объявлении: https://godbolt.org/z/i2aQ5Z
Но это быстро станет громоздким в обслуживаниикак только число специализаций возрастет.
Другой, возможно, немного более обслуживаемый вариант - возвращать что-то вроде ret_type<N>::type
и специализировать его вместе с bar
.Но он все равно не будет таким же чистым, как при использовании auto
.