Аргументи шаблону за замовчуванням для шаблонів функцій


187

Чому аргументи шаблону за замовчуванням дозволені лише на шаблонах класів? Чому ми не можемо визначити тип за замовчуванням у шаблоні функції члена? Наприклад:

struct mycclass {
  template<class T=int>
  void mymember(T* vec) {
    // ...
  }
};

Натомість C ++ примушує аргументи шаблону за замовчуванням дозволені лише на шаблоні класу.


8
+1 Це справді складне питання.
АраК

1
Для перших трьох опублікованих відповідей розглянемо цей приклад: struct S { template <class R = int> R get_me_R() { return R(); } };Параметр шаблону неможливо вивести з контексту.
AraK

3
Гарне питання. 3 люди вже відповіли, що сказали, що "не має сенсу", і вони взагалі помиляються. Параметри шаблону функцій не завжди можна вивести з параметрів виклику функції. Наприклад, якщо вони дозволили, я міг би написати template <int N = 1> int &increment(int &i) { i += N; return i; }, а потім increment(i);або increment<2>(i);. Як є, я мушу писати increment<1>(i);.
Стів Джессоп

Насправді, мої та приклади AraK можна вирішити, перевантажившись. думаю, litb не може, тому що параметр шаблона може бути виведений або може бути визначений.
Стів Джессоп

3
@Steve: Відсутня крапка з комою є фактично новим перевантаженням оператора EOL для доповнення Б. Ставтрупа "Перевантаження C ++ пробілів", опублікованого в журналі "Об'єктно-орієнтоване програмування", 1 квітня 1992 р. ( Www2.research.att.com/~bs/ paper.html )

Відповіді:


148

Має сенс навести аргументи шаблону за замовчуванням. Наприклад, ви можете створити функцію сортування:

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 ++. Дивіться цей звіт про дефекти Bjarne Stroustrup: Аргументи шаблонів за замовчуванням для шаблонів функцій та те, що він говорить

Заборона аргументів шаблонів за замовчуванням для шаблонів функцій є помилковим залишком часу, коли автономні функції розглядалися як громадяни другого класу і вимагали, щоб усі аргументи шаблону виводилися з аргументів функції, а не були вказані.

Обмеження серйозно стискає стиль програмування, необґрунтовано роблячи незалежні функції, відмінні від функцій-членів, тим самим ускладнюючи запис коду в стилі STL.


@Arman, посилання звіту про дефекти містить зміни, які вносяться до робочого проекту для C ++ 0x та обговорень. Аргументи, ні виведені, ні чітко визначені, не отримані з аргументів за замовчуванням. GCC4.4 підтримує аргументи за замовчуванням для шаблонів функцій у режимі C ++ 0x.
Йоханнес Шауб - ліб

4
Нічого спільного з запитанням чи відповіддю, але Герб Саттер назвав актуальний стандарт C ++ 11 після останньої зустрічі в суботу. Я просто читаю це сьогодні і відчуваю, що поділюсь
Девід Родрігес - дрибес

і обов'язкове подальше запитання ... коли очікується, що це стане дорогою до інших компіляторів :)
Джеймі Кук

@ JohannesSchaub-litb У мене була така ж проблема: немає можливості вказати тип за замовчуванням у функції шаблону. Я вирішив явною інстанцією функції за типом за замовчуванням ( doubleу моєму випадку). Можливо, це не "загально", але чи є якийсь недолік у цій практиці? Дякую.
JackOLantern

У наступному коді не вдається зібрати такі помилки, як error: invalid conversion from ‘int’ to ‘int*’будь-яка ідея, чому: `#include <array> #include <algorithm> #include <functional> template <typename Iterator, typename Comp = std :: less <Iterator>> void my_sort ( Iterator beg, Iterator end, Comp c = Comp ()) {std :: sort (попросити, закінчити, c); } int main () {std :: array <int, 5> ar {5,2,21,7,4}; my_sort (ar.begin (), ar.end ()); } `
Люк Петерсон

36

Для цитування шаблонів C ++: Повне керівництво (стор. 207):

Коли шаблони були спочатку додані до мови C ++, явні аргументи шаблону функції не були дійсною конструкцією. Аргументи шаблону функцій завжди повинні були виводитися з виразу виклику. В результаті, здавалося, немає жодної переконливої ​​причини, щоб дозволити аргументи шаблону функції за замовчуванням, тому що за замовчуванням завжди буде замінено виведене значення.


простий і стислий :)
InQusitive

17

Поки всі запропоновані приклади параметрів шаблонів за замовчуванням для шаблонів функцій можна робити з перевантаженнями.

AraK:

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); }

літ:

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, додавання невиведених параметрів було занадто пізно, щоб хтось зрозумів та / або дійсно оцінив, що він зробив корисні за замовчуванням корисні. Отже, поточна ситуація діє на основі версії шаблонів функцій, яка ніколи не була стандартною.


@Steve: Значить, яйце бігло швидше, ніж курка? :) цікаво. Дякую.
Арман

1
Напевно, лише одна з таких речей. Процес стандартизації C ++ частково проходить повільно, так що люди встигають усвідомити, коли зміна створює можливості або труднощі в іншому місці стандарту. Сподіваємося, труднощі стикаються з людьми, які впроваджують проект стандарту, коли йдуть далі, коли помічають суперечності чи неоднозначність. Можливості дозволити речі, які раніше не дозволяли, покладайтеся на того, хто хоче написати код, помітивши, що він більше не повинен бути незаконним ...
Стів Джессоп

2
Ще один для вас: template<typename T = void> int SomeFunction();. Параметр шаблону тут ніколи не використовується, а насправді функція ніколи не викликається; єдине місце , де його називають це в decltypeабо sizeof. Назва свідомо відповідає імені іншої функції, але факт, що це шаблон, означає, що компілятор віддасть перевагу вільній функції, якщо вона існує. Вони використовуються в SFINAE для надання поведінки за замовчуванням, коли відсутнє визначення функції.
Том

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

Детальніше дивіться тут .


1
Зауважте, що, хоча це не дозволяє "аргументи шаблону за замовчуванням дозволені лише на шаблоні класу шаблону", це фактично не примушує процес ідентифікації шаблону використовувати надане значення. Для цього потрібен VS2013 (або будь-який інший компілятор, який завершив дефект C ++ 11 226 "Аргументи шаблону за замовчуванням для шаблонів функцій")
puetzk

1

Я використовую наступний трюк:

Скажімо, ви хочете мати функцію так:

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();

Як ми бачимо, не потрібно чітко встановлювати другий параметр. Можливо, комусь це стане в нагоді.


Це точно не відповідь.
Щеня

@deadmg - ви можете пояснити, чому? Ми не всі гуру шаблонів C ++. Дякую.
Кев

Це вирішення, яке є дуже акуратним, але не охоплюватиме всі випадки, які вам можуть захотіти. Наприклад, як би ви застосували це до конструктора?
Тіберіу Савін

@TiberiuSavin - якщо я правильно вас зрозумів, то ви можете зробити так: шаблон <typename E, typename ARR_E> worker <E, ARR_E> :: worker (ARR_E * parr) {parr_ = parr; }. А потім використовуйте його так: worker <int> w2 (& my_array);
alariq
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.