Специализация шаблона и явная спецификация типа возвращаемого значения против auto - PullRequest
0 голосов
/ 23 сентября 2018

Рассмотрим код:

class Test {
public:
    template<int N> auto foo() {}
    template<> auto foo<0>() { return 7;  }

    template<int N> void bar() {}
    template<> int bar<0>() { return 7;  }
};

Я тестировал код с различными компиляторами (через Проводник компилятора ).

В случае Clang 7.0.0 foo компилируется, в то время как bar выдает ошибку:

: 8: 20: ошибка: ни один шаблон функции не соответствует специализации шаблона функции 'bar'

template<> int bar<0>() { return 7;  }

               ^

: 7: 26: примечание: шаблон кандидата игнорируется: не удалось сопоставить 'void ()' с 'int ()'

template<int N> void bar() {};

                     ^

Visual C ++ согласен(MSVC 19 2017 RTW):

(8): ошибка C2912: явная специализация 'int Test :: bar (void)' не является специализацией шаблона функции

gcc 8.2 не компилирует ни один код (хотя причина, вероятно, заключается в ошибке в поддержке C ++ 17 :

: 5:14: ошибка: явная специализация в области отсутствия пространства имен 'Тест класса'

 template<> auto foo<0>() { return 7;  };

          ^

: 5: 28: ошибка: идентификатор шаблона 'foo <0>' в объявлении первичного шаблона

 template<> auto foo<0>() { return 7;  };

                        ^

: 7: 26: ошибка: слишком многоny template-parameter-lists

 template<int N> void bar() {};

                      ^~~

: 8: 14: ошибка: явная специализация в области отсутствия пространства имен 'class Test'

 template<> int bar<0>() { return 7;  }

          ^

: 8: 20: ошибка: ожидается';' в конце объявления члена

 template<> int bar<0>() { return 7;  }

                ^~~

                   ;

: 8: 23: ошибка: ожидаемый неквалифицированный идентификатор перед символом <<* </p>

 template<> int bar<0>() { return 7;  }

                   ^

Что такое правильная интерпретацияВот?Могу ли я иметь другой тип возвращаемого значения для разных специализаций метода (и почему только с auto, но не указав их явно)?С моим ограниченным пониманием auto и шаблонов я бы сказал «нет».Я не понимаю, почему использование auto вместо явного именования возвращаемого типа позволило бы иметь разные возвращаемые типы для разных специализаций.

Однако эти коды являются упрощенными версиями кода, который я нашел в другом месте , поэтому, возможно, моя интерпретация неверна - и в этом случае я был бы благодарен за объяснение почемуРазличный тип возвращаемого значения допускается, когда auto используется для специализации, в то время как явное именование типа представляется запрещенным .

1 Ответ

0 голосов
/ 26 сентября 2018

Существует несколько разных проблем с примером кода.

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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...