ChatGPT解决这个技术问题 Extra ChatGPT

Default template arguments for function templates

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.

For the first three posted answers, consider this example: struct S { template <class R = int> R get_me_R() { return R(); } }; The template parameter can't be deduced from the context.
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 N = 1> 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);.
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, hmm you can write template<typename Iterator> void sort(Iterator beg, Iterator end) { sort(beg, end, std::less<Iterator>()); } 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)
@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. (www2.research.att.com/~bs/papers.html)

J
Johannes Schaub - litb

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.


@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.
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 :) herbsutter.wordpress.com/2010/03/13/…
and the obligatory follow up question... when is this expected to make it's way into other compilers :)
@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.
The following code fails to compile, with errors like error: invalid conversion from ‘int’ to ‘int*’, any idea why: ` #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(beg, end, c); } int main() { std::array<int, 5> ar{5,2,21,7,4}; my_sort(ar.begin(), ar.end()); } `
J
James McNellis

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.


S
Steve Jessop

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: So the egg was running faster than chicken?:) interesting. Thanks.
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...
One more for you: template<typename T = void> 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.
A
Adi Shavit

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.


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")
E
Edgar Rokjān

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.


This is definitely not an answer.
@deadmg - can you explain why? We're not all C++ template gurus. Thanks.
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?
@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);