Аргументы шаблона по умолчанию для шаблонов функций
Почему аргументы шаблона по умолчанию разрешены только для шаблонов классов? Почему мы не можем определить тип по умолчанию в шаблоне функции-члена? Например:
struct mycclass {
template<class T=int>
void mymember(T* vec) {
// ...
}
};
Вместо этого С++ заставляет использовать аргументы шаблона по умолчанию только для шаблона класса.
Ответы
Ответ 1
Имеет смысл дать аргументы шаблона по умолчанию. Например, вы можете создать функцию сортировки:
template<typename Iterator,
typename Comp = std::less<
typename std::iterator_traits<Iterator>::value_type> >
void sort(Iterator beg, Iterator end, Comp c = Comp()) {
...
}
С++ 0x вводит их в С++. См. Этот отчет о дефектах Бьярне Страуступа: Аргументы шаблонов по умолчанию для шаблонов функций и то, что он говорит
Запрет аргументов шаблона по умолчанию для шаблонов функций является ошибочным остатком того времени, когда автономные функции рассматривались как граждане второго сорта и требовали, чтобы все аргументы шаблона были выведены из аргументов функции, а не указаны.
Ограничение серьезно затрудняет стиль программирования, излишне делая независимые функции отличными от функций-членов, тем самым затрудняя запись кода в стиле STL.
Ответ 2
Чтобы процитировать С++ Templates: The Complete Guide (стр. 207):
Когда шаблоны были первоначально добавлены на язык С++, явные аргументы шаблона функции не были допустимой конструкцией. Аргументы шаблона функции всегда должны выводиться из выражения вызова. В результате, по-видимому, не было веских оснований разрешать аргументы шаблона функции по умолчанию, потому что значение по умолчанию всегда было бы переопределено выведенным значением.
Ответ 3
До сих пор все предлагаемые примеры параметров шаблона по умолчанию для шаблонов функций можно выполнять с помощью перегрузок.
арак:
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, а вызов с комментариями не может скомпилироваться, как ожидалось.
Поэтому я подозреваю, что параметры шаблона по умолчанию "не нужны", но, вероятно, только в том же смысле, что аргументы функции по умолчанию "не нужны". Как указывает отчет о дефектах Stroustrup, добавление не выводимых параметров было слишком запоздалым для того, чтобы кто-либо осознал и/или действительно оценил, что он сделал дефолты полезными. Таким образом, текущая ситуация действует на основе версии шаблонов функций, которая никогда не была стандартной.
Ответ 4
В Windows со всеми версиями Visual Studio вы можете преобразовать эту ошибку (C4519) в предупреждение или отключить ее так:
#ifdef _MSC_VER
#pragma warning(1 : 4519) // convert error C4519 to warning
// #pragma warning(disable : 4519) // disable error C4519
#endif
Подробнее здесь.
Ответ 5
Я использую следующий трюк:
Предположим, вы хотите иметь такую функцию:
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();
Как мы видим, нет необходимости явно устанавливать второй параметр.
Может быть, это будет полезно для кого-то.