Объявления специализации шаблона функции C++ и аргументы шаблона; нет против <> против <тип>
При изучении шаблонов функций я вижу специализации, объявленные по-разному:
template<> void f(argtype) {}
template<> void f<>(argtype) {}
template<> void f<argtype>(argtype) {}
... и мне интересно, чем они отличаются. Учитывая приведенный ниже пример с шаблонными функциями с параметром и без него, у меня есть несколько вопросов.
#include <iostream>
#include <typeinfo>
//Function print1 WITH function parameter---------------------------------------------
template<class T>
void print1(T) { std::cout << "Primary template for print1() with type " << typeid(T).name() << std::endl; }
template<>
void print1<int>(int) { std::cout << "Specialization for print1<int>(int)" << std::endl; }
//Not allowed, deduced to be the same as print1<int>(int)
/*template<>
void print1<>(int) { std::cout << "Specialization for print1<>(int)" << std::endl; }*/
//Not allowed, deduced to be the same as print1<int>(int)
/*template<>
void print1(int) { std::cout << "Specialization for print1(int)" << std::endl; }*/
//Function print2 WITHOUT function parameter------------------------------------------
/*Not allowed together with print<>(); compiler complains:
t2.cpp:29:6: error: template-id 'print2<>' for 'void print2()' does not match any template declaration*/
/*template<class T>
void print2() { std::cout << "Primary template for print2()" << std::endl; }*/
template<class T = short> //Declaration of print2<>() now ok in conjunction with print2<>()
void print2() { std::cout << "Primary template for print2()" << std::endl; }
template<>
void print2<int>() { std::cout << "Specialization for print2<int>()" << std::endl; }
template<>
void print2<>() { std::cout << "Specialization for print2<>()" << std::endl; }
int main() {
//These three work in the same way, no matter which call method we use, so far so good
print1(10);
print1<>(10);
print1<int>(10);
print1(true);
print1<>(true);
print1<bool>(true);
print2(); //Triggers print2<>(), a bit unexpectedly, should trigger print2<short>() (primary template)
print2<>(); //Triggers print2<>(), a bit unexpectedly, should trigger print2<short>() (primary template)
print2<bool>(); //Triggers print2<bool>() primary template
print2<short>(); //Triggers print2<>(), should definately trigger primary template for print2()
print2<int>(); //Triggers print2<int>() specialization
return 0;
}
выходы:
Specialization for print1<int>(int)
Specialization for print1<int>(int)
Specialization for print1<int>(int)
Primary template for print1() with type b
Primary template for print1() with type b
Primary template for print1() with type b
Specialization for print2<>()
Specialization for print2<>()
Primary template for print2()
Specialization for print2<>()
Specialization for print2<int>()
- Какой особый смысл получается, если оставить аргумент специализации шаблона пустым, несуществующим или со специализированным типом и как это влияет на результат? Кажется, что с аргументом функции эта спецификация является излишней, и компилятор выводит ее независимо от того, как она указана (в результате эквивалентные явные спецификации становятся недопустимыми переопределениями).
- Я понимаю, что при наличии функции без параметров специальный аргумент шаблона необходим явно в объявлении, чтобы указать, к какому экземпляру относится определенная функция (поскольку в противном случае ее нельзя вывести). Но значение, кажется, подразумевает нечто большее в этом случае, и "пустая" специализация (<>) запускается несколько непредвиденными способами. Как так?
- Почему у меня должен быть параметр шаблона по умолчанию при специализации print2 с print2<>(), но не без него?
2 ответа
Какой особый смысл получается, если оставить аргумент специализации шаблона пустым, несуществующим или со специализированным типом и как это влияет на результат?
Если вы предоставляете список аргументов шаблона полностью, то вы просто явно специализируете шаблон функции для данного набора аргументов шаблона.
Если вы предоставляете аргументы для (возможно, пустого) подмножества параметров шаблона, то вы явно специализируете шаблон функции для набора аргументов, которые должны быть выведены. Согласно [temp.deduct.decl]:
В объявлении, идентификатор объявления которого ссылается на специализацию шаблона функции, для определения специализации, на которую ссылается объявление, выполняется вывод аргумента шаблона. В частности, это делается для явных экземпляров (14.7.2), явных специализаций (14.7.3) и некоторых объявлений друзей (14.5.4). [...]. Во всех этих случаях
P
тип шаблона функции, который рассматривается как потенциальное совпадение иA
это […] тип функции из объявления […].
Вычет производится, как описано в 14.8.2.5.Если для рассматриваемого набора шаблонов функций нет совпадения или более одного совпадения после частичного упорядочения (14.5.6.2), вычет не выполняется, и в случаях объявления программа не сформирована.
Таким образом, для каждого параметра, для которого не был задан аргумент, или в случае, когда список не указан вообще, вычитание аргумента шаблона выполняется для каждого соответствующего параметра специализации и его аналога из основного шаблона. Процесс описан в §14.8.2.5 и работает так же, как если бы мы вызывали основной шаблон с предоставленным списком аргументов шаблона в качестве аргументов шаблона, а объекты типов параметров в специализации в качестве аргументов функции.
Вы должны быть знакомы с тем фактом, что можно вызвать шаблон функции с некоторыми из указанных аргументов шаблона, например,
template <typename A, typename B> void foo(A, B);
foo(7684, 48.);
foo<int>(7684, 48.);
foo<int, double>(7684, 48.);
Это работает аналогично для явных специализаций:
template <typename T, typename U>
void foo(T, U) {}
// Both template arguments have to be deduced.
template<> void foo(double, float);
// The *exact* same as above.
// template<> void foo<>(double, float);
// Second template argument has to be deduced by type.
// If we call foo<int>(int(), float()) then the deduced specialization is
// foo<int, float>, thus U=float.
template<> void foo<int>(int, float);
template<> void foo<int, int>(int, int);
Это также может применяться к перегрузкам шаблона функции. В попытке найти основной шаблон, которому соответствует специализация, выбирается наиболее специализированный.
template <typename T, typename U>
void foo(T&, U&) {}
template <typename T, typename U>
void foo(T const&, U&) {}
// Specializes the second overload because it is more specialized.
template <>
void foo(int const&, float&);
Обратите внимание, что при поиске первичного шаблона предоставленные аргументы (т. Е. Не выводимые) используются для проверки результирующего параметра функции первичного шаблона с результирующим параметром функции специализации. Они должны быть равны.
template <typename T, typename U>
void foo(T&, U&) {}
// Error - no matching primary template found.
template <>
void foo<int, int>(float&, int&);
// Dito:
template <>
void foo<int>(int, int&);
Кажется, что с аргументом функции эта спецификация является излишней, и компилятор выводит ее независимо от того, как она указана (в результате эквивалентные явные спецификации становятся недопустимыми переопределениями).
Да, это действительно так. Учтите, что если вы укажете неверный аргумент шаблона, это приведет к ошибке:
Но значение, кажется, подразумевает нечто большее в этом случае, и "пустая" специализация (<>) запускается несколько непредвиденными способами. Как так?
Для вызова аргументы шаблона выводятся первыми. Затем вызывается специализация с этими аргументами шаблона.
Если вы явно специализировали шаблон функции для этой конкретной специализации, то вот print2<>
который print2<short>
тогда эта явная специализация называется.
Как это непредвиденно?
Почему при специализации у меня должен быть параметр шаблона по умолчанию
print2
сprint2<>()
но не без этого?
Потому что компилятор не может вывести аргумент. Если вы предоставляете аргумент по умолчанию, он не должен выводить его в первую очередь.
Какой особый смысл получается, если оставить аргумент специализации шаблона пустым
Недостающие аргументы выводятся по возможности; пустой список аргументов означает, что все аргументы должны быть выведены.
не существует
Это означает, что вы объявляете основной шаблон, а не явную специализацию.
или со специализированным типом
Это означает, что вы объявляете явную специализацию для этого типа.
"пустая" специализация (<>) вызвана несколько непредвиденными способами. Как так?
В обоих случаях аргумент шаблона выводится. В print1
, основной шаблон заявляет, что T
тот же тип, что и параметр функции; так что это выводится из параметра функции. В print2
, он объявлен с типом по умолчанию short
так что используется. Так что ваше удивление при виде print2<>
когда вы думаете, что это должно быть print2<short>
объясняется: print2<>
является print2<short>
,
Почему у меня должен быть параметр шаблона по умолчанию при специализации print2 с print2<>(), но не без него?
Если бы не было ни аргумента по умолчанию, ни параметров функции, из которых можно вывести аргумент, то было бы невозможно вывести тип для специализации, поэтому <>
не может быть использован. Я не знаю, что вы подразумеваете под "без него"; если вы имеете в виду "без <>
msgstr ", то вы объявляете основной шаблон, а не специализацию, а параметр является общим.