Аргументы шаблона по умолчанию для шаблонов функций
Почему аргументы шаблона по умолчанию разрешены только в шаблонах классов? Почему мы не можем определить тип по умолчанию в шаблоне функции-члена? Например:
struct mycclass {
template<class T=int>
void mymember(T* vec) {
// ...
}
};
Вместо этого C++ принудительно разрешает использовать аргументы шаблона по умолчанию только в шаблоне класса.
5 ответов
Имеет смысл указать аргументы шаблона по умолчанию. Например, вы можете создать функцию сортировки:
template<typename Iterator,
typename Comp = std::less<
typename std::iterator_traits<Iterator>::value_type> >
void sort(Iterator beg, Iterator end, Comp c = Comp()) {
...
}
C++ 0x знакомит их с C++. Смотрите этот отчет о дефектах Бьярна Страуструпа: Аргументы шаблона по умолчанию для шаблонов функций и что он говорит
Запрещение аргументов шаблона по умолчанию для шаблонов функций является ошибочным пережитком времени, когда автономные функции рассматривались как граждане второго сорта и требовали, чтобы все аргументы шаблона выводились из аргументов функции, а не указывались.
Ограничение серьезно ограничивает стиль программирования тем, что ненужные функции отличаются от функций-членов, что затрудняет написание кода в стиле STL.
Цитировать шаблоны C++: Полное руководство (стр. 207):
Когда шаблоны первоначально добавлялись в язык C++, явные аргументы шаблонов функций не были допустимой конструкцией. Аргументы шаблона функции всегда должны были выводиться из выражения вызова. В результате, казалось, нет веской причины разрешать аргументы шаблона функции по умолчанию, потому что значение по умолчанию всегда будет переопределено выведенным значением.
Пока что все предложенные примеры параметров шаблона по умолчанию для шаблонов функций могут быть выполнены с перегрузками.
арак:
struct S {
template <class R = int> R get_me_R() { return R(); }
};
может быть:
struct S {
template <class R> R get_me_R() { return R(); }
int get_me_R() { return int(); }
};
Мой собственный:
template <int N = 1> int &increment(int &i) { i += N; return i; }
может быть:
template <int N> int &increment(int &i) { i += N; return i; }
int &increment(int &i) { return increment<1>(i); }
LITB:
template<typename Iterator, typename Comp = std::less<Iterator> >
void sort(Iterator beg, Iterator end, Comp c = Comp())
может быть:
template<typename Iterator>
void sort(Iterator beg, Iterator end, std::less<Iterator> c = std::less<Iterator>())
template<typename Iterator, typename Comp >
void sort(Iterator beg, Iterator end, Comp c = Comp())
Страуструп:
template <class T, class U = double>
void f(T t = 0, U u = 0);
Может быть:
template <typename S, typename T> void f(S s = 0, T t = 0);
template <typename S> void f(S s = 0, double t = 0);
Что я доказал с помощью следующего кода:
#include <iostream>
#include <string>
#include <sstream>
#include <ctype.h>
template <typename T> T prettify(T t) { return t; }
std::string prettify(char c) {
std::stringstream ss;
if (isprint((unsigned char)c)) {
ss << "'" << c << "'";
} else {
ss << (int)c;
}
return ss.str();
}
template <typename S, typename T> void g(S s, T t){
std::cout << "f<" << typeid(S).name() << "," << typeid(T).name()
<< ">(" << s << "," << prettify(t) << ")\n";
}
template <typename S, typename T> void f(S s = 0, T t = 0){
g<S,T>(s,t);
}
template <typename S> void f(S s = 0, double t = 0) {
g<S,double>(s, t);
}
int main() {
f(1, 'c'); // f<int,char>(1,'c')
f(1); // f<int,double>(1,0)
// f(); // error: T cannot be deduced
f<int>(); // f<int,double>(0,0)
f<int,char>(); // f<int,char>(0,0)
}
Вывод на печать соответствует комментариям для каждого вызова функции f, а закомментированный вызов не может быть скомпилирован должным образом.
Поэтому я подозреваю, что параметры шаблона по умолчанию "не нужны", но, вероятно, только в том смысле, в котором аргументы функций по умолчанию "не нужны". Как указывается в отчете о дефектах Страуструпа, добавление невыгруженных параметров было слишком поздно для того, чтобы кто-либо осознал и / или действительно оценил, что он сделал полезными значения по умолчанию. Таким образом, текущая ситуация основана на версии шаблонов функций, которая никогда не была стандартной.
В Windows во всех версиях Visual Studio вы можете преобразовать эту ошибку ( C4519) в предупреждение или отключить ее следующим образом:
#ifdef _MSC_VER
#pragma warning(1 : 4519) // convert error C4519 to warning
// #pragma warning(disable : 4519) // disable error C4519
#endif
Смотрите подробности здесь.
Я использую следующий трюк:
Допустим, вы хотите иметь такую функцию:
template <typename E, typename ARR_E = MyArray_t<E> > void doStuff(ARR_E array)
{
E one(1);
array.add( one );
}
Вам не разрешат, но я поступлю следующим образом:
template <typename T>
struct MyArray_t {
void add(T i)
{
// ...
}
};
template <typename E, typename ARR_E = MyArray_t<E> >
class worker {
public:
/*static - as you wish */ ARR_E* parr_;
void doStuff(); /* do not make this one static also, MSVC complains */
};
template <typename E, typename ARR_E>
void worker<E, ARR_E>::doStuff()
{
E one(1);
parr_->add( one );
}
Таким образом, вы можете использовать это так:
MyArray_t<int> my_array;
worker<int> w;
w.parr_ = &arr;
w.doStuff();
Как видим, нет необходимости явно устанавливать второй параметр. Может быть, это будет кому-то полезно.