197

Why are default template arguments only allowed on class templates? Why can't we define a default type in a member function template? For example:

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

Instead, C++ forces that default template arguments are only allowed on a class template.

Luc Danton
  • 34,649
  • 6
  • 70
  • 114
Arman
  • 4,566
  • 10
  • 45
  • 66
  • 1
    For the first three posted answers, consider this example: `struct S { template R get_me_R() { return R(); } };` The template parameter can't be deduced from the context. – Khaled Alshaya Mar 15 '10 at 13:46
  • 3
    Good question. 3 people have already answered to say it "doesn't make sense", and they're all wrong in general. Function template parameters are *not* always deductible from the function call parameters. For example, if they were allowed I could write `template int &increment(int &i) { i += N; return i; }`, and then `increment(i);` or `increment<2>(i);`. As it is, I have to write `increment<1>(i);`. – Steve Jessop Mar 15 '10 at 13:48
  • Actually, mine and AraK's examples can both be dealt with by overloading. litb's can't, I think, because the template parameter *might* be deduced or might be specified. – Steve Jessop Mar 15 '10 at 13:57
  • @Steve, hmm you can write `template void sort(Iterator beg, Iterator end) { sort(beg, end, std::less()); }` and write the three-args as an overload. I think it's done that way in today's `std::sort`. (aww, i should have passed `value_type` to std::less xD) – Johannes Schaub - litb Mar 15 '10 at 14:04
  • std::sort is done that way now, but your definition allows something that the current definition doesn't - specifying 2 (of 2) template parameters but only 2 (of 3) function call arguments. But I wrongly thought that means your example *can't* be done with overloads, actually it merely *isn't* done with overloads currently since the 2-template-param overload doesn't have a default value for its 3rd param. The example in the defect report can be done with overloads (I checked, finding a missing ; in the example text in the process). – Steve Jessop Mar 15 '10 at 14:30
  • 3
    @Steve: The missing semicolon is actually a new EOL operator overload to complement B. Stavtrup's "Overloading of C++ Whitespace" published in Journal of Object-Oriented Programming, April 1, 1992. (http://www2.research.att.com/~bs/papers.html) –  Mar 15 '10 at 14:43

5 Answers5

154

It makes sense to give default template arguments. For example you could create a sort function:

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 introduces them to C++. See this defect report by Bjarne Stroustrup: Default Template Arguments for Function Templates and what he says

The prohibition of default template arguments for function templates is a misbegotten remnant of the time where freestanding functions were treated as second class citizens and required all template arguments to be deduced from the function arguments rather than specified.

The restriction seriously cramps programming style by unnecessarily making freestanding functions different from member functions, thus making it harder to write STL-style code.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • @Arman, the defect report link contains the changes that are made to the working draft for C++0x and the discussions. Arguments neither deduced nor explicitly specified are obtained from default arguments. GCC4.4 supports default arguments for function templates in C++0x mode. – Johannes Schaub - litb Mar 15 '10 at 13:59
  • 4
    Nothing to do with the question or answer, but Herb Sutter called the upcomming standard C++11 after last saturdays meeting. I just read it today and feel like sharing :) http://herbsutter.wordpress.com/2010/03/13/trip-report-march-2010-iso-c-standards-meeting/ – David Rodríguez - dribeas Mar 15 '10 at 14:05
  • and the obligatory follow up question... when is this expected to make it's way into other compilers :) – Jamie Cook Dec 10 '10 at 05:13
  • @JohannesSchaub-litb I had the same problem: no possibility to speficy the default type in a template function. I have solved by an explicit instantiation of the function on the default type (`double` in my case). Perhaps it is not "general", but is there any drawback with this practice? Thanks. – Vitality May 05 '13 at 18:33
  • The following code fails to compile, with errors like `error: invalid conversion from ‘int’ to ‘int*’`, any idea why: ` #include #include #include template > void my_sort(Iterator beg, Iterator end, Comp c = Comp()) { std::sort(beg, end, c); } int main() { std::array ar{5,2,21,7,4}; my_sort(ar.begin(), ar.end()); } ` – Luke Peterson Oct 04 '14 at 20:48
  • @hazelnusse please make a new stackoverflow question for that. the compiler error in that snippet is orthogonal to this question I answered. – Johannes Schaub - litb Oct 05 '14 at 10:30
  • @JohannesSchaub-litb, while you are technically correct, most sorts don't compare iterators but instead the values pointed to by the iterators. Changing `std::less` to `std::less::value_type>` fixes the issue. – Luke Peterson Oct 07 '14 at 07:17
  • @hazelnusse ah I'm sorry I didn't notice the code you gave me was partly the code of my answer. Of course there was a technical error in my code. Fixed – Johannes Schaub - litb Oct 10 '14 at 21:31
  • Using modern C++ the template header can be simplified to just `template>`. – user515430 Nov 07 '18 at 23:20
  • Is your answer still valid with C++11 C++14 or C++17? – Minimus Heximus Mar 08 '19 at 19:01
38

To quote C++ Templates: The Complete Guide (page 207):

When templates were originally added to the C++ language, explicit function template arguments were not a valid construct. Function template arguments always had to be deducible from the call expression. As a result, there seemed to be no compelling reason to allow default function template arguments because the default would always be overridden by the deduced value.

James McNellis
  • 348,265
  • 75
  • 913
  • 977
18

So far, all the proffered examples of default template parameters for function templates can be done with overloads.

AraK:

struct S { 
    template <class R = int> R get_me_R() { return R(); } 
};

could be:

struct S {
    template <class R> R get_me_R() { return R(); } 
    int get_me_R() { return int(); }
};

My own:

template <int N = 1> int &increment(int &i) { i += N; return i; }

could be:

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

could be:

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

Stroustrup:

template <class T, class U = double>
void f(T t = 0, U u = 0);

Could be:

template <typename S, typename T> void f(S s = 0, T t = 0);
template <typename S> void f(S s = 0, double t = 0);

Which I proved with the following code:

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

The printed output matches the comments for each call to f, and the commented-out call fails to compile as expected.

So I suspect that default template parameters "aren't needed", but probably only in the same sense that default function arguments "aren't needed". As Stroustrup's defect report indicates, the addition of non-deduced parameters was too late for anyone to realise and/or really appreciate that it made defaults useful. So the current situation is in effect based on a version of function templates which was never standard.

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
  • @Steve: So the egg was running faster than chicken?:) interesting. Thanks. – Arman Mar 15 '10 at 15:08
  • 1
    Probably just one of those things. The C++ standardisation process runs slow in part so that people have time to realise when a change creates opportunities or difficulties elsewhere in the standard. Difficulties hopefully are caught by the people implementing the draft standard as they go along, when they spot a contradiction or ambiguity. Opportunities to allow things that weren't allowed before, rely on someone who wants to write the code noticing that it no longer needs to be illegal... – Steve Jessop Mar 15 '10 at 15:22
  • 2
    One more for you: `template int SomeFunction();`. The template parameter here is never used, and in fact the function is never called; the only place it is referred to is in a `decltype` or `sizeof`. The name deliberately matches the name of another function but the fact it's a template means the compiler will prefer the free function if it exists. The two are used in SFINAE to provide default behaviour where a function definition is missing. – Tom Aug 07 '12 at 14:05
3

On Windows, with all versions of Visual Studio you can convert this error (C4519) to a warning or disable it like so:

#ifdef  _MSC_VER
#pragma warning(1 : 4519) // convert error C4519 to warning
// #pragma warning(disable : 4519) // disable error C4519
#endif

See more details here.

Adi Shavit
  • 16,743
  • 5
  • 67
  • 137
  • 2
    Note that, while this does disable the "default template arguments are only allowed on a class template" message, it doesn't actually make the template instantiation process *use* the provided value. That requires VS2013 (or any other compiler that has completed C++11 defect 226 "Default template arguments for function templates") – puetzk Jan 19 '15 at 05:11
1

What I use is next trick:

Lets say you want to have function like this:

template <typename E, typename ARR_E = MyArray_t<E> > void doStuff(ARR_E array)
{
    E one(1);
    array.add( one );
}

You will not be allowed, but I do next way:

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

So this way you may use it like this:

MyArray_t<int> my_array;
worker<int> w;
w.parr_ = &arr;
w.doStuff();

As we can see no need to explicitly set second parameter. Maybe it will be useful for someone.

Edgar Rokjān
  • 17,245
  • 4
  • 40
  • 67
alariq
  • 496
  • 3
  • 10
  • This is definitely not an answer. – Puppy Jul 23 '11 at 11:51
  • @deadmg - can you explain why? We're not all C++ template gurus. Thanks. – Kev Jul 23 '11 at 12:24
  • This is a workaround which is pretty neat but it won't cover all the cases you might want. For example how would you apply this to a constructor? – Tiberiu Savin Aug 20 '13 at 01:00
  • @TiberiuSavin - if I understood you correctly, then you can do like this: template worker::worker(ARR_E* parr) { parr_ = parr; }. And then use it like this: worker w2(&my_array); – alariq Nov 14 '13 at 18:11