15

C++ standard section 8.3.6.4 says that

For non-template functions, default arguments can be added in later declarations of a function in the same scope. [...]

But my question is that why it isn't allowed for template functions? What is the rationale for not allowing addition of default arguments in later declarations in same scope for template functions?

Consider this program which compiles fine. (non template function) (see live demo here.)

#include <iostream>

int f(int a,int b,int c=3);
int f(int a,int b=9,int c); // default argument in middle, ok allowed

int main()
{
    f(3);
    f(3,6);
    f(3,6,9);
    return 0;
}

int f(int a,int b,int c)
{
    std::cout<<a<<' '<<b<<' '<<c<<'\n';
    return 0;
}

But following fails in compilation. (template function) (see live demo here.)

#include <iostream>

template <typename T> 
void f(T a,int b,int c=3);
template <typename T> 
void f(T a,int b=9,int c); // compiler error why???

int main()
{
    f(3);
    f(3,6);
    f(3,6,9);
    return 0;
}

template <typename T> 
void f(T a,int b,int c)
{
    std::cout<<a<<' '<<b<<' '<<c<<'\n';
} 
Destructor
  • 14,123
  • 11
  • 61
  • 126
  • 2
    Missing `template `? – LogicStuff Sep 02 '15 at 17:20
  • Isn't that less *addition* and more *redefinition* of default arguments? – jaggedSpire Sep 02 '15 at 17:20
  • 2
    [Here's](http://coliru.stacked-crooked.com/a/39117b9aa49c90a4) an almost-compiling example – jaggedSpire Sep 02 '15 at 17:23
  • 2
    @PravasiMeet I was referring to the second declaration, which in addition to the middle argument's new default argument, you've redefined the last argument's default value. Since in the *first* example, you don't redefine the `c` argument, it is rather odd you'd do it in the second. Especially sine *redefinition* of a default argument is its own compilation error. – jaggedSpire Sep 02 '15 at 17:24
  • 3
    Better question might be why is it allowed for non-template functions... – Barry Sep 02 '15 at 17:25
  • 1
    @PravasiMeet Becuase you're either declaring or declaring and defining a templated function. Leave off the `template `, you're declaring (or declaring and defining) a function which doesn't have a template parameter. – jaggedSpire Sep 02 '15 at 17:28

2 Answers2

12

This is a historical limitation that was added fairly early in the standardization process (it was there in C++98, but not in the ARM).

I don't recall the exact reason (and neither does my colleague, who was almost certainly there when the decision was made). However, I have a guess...

Back then, all-but-one compilers instantiated templates by replaying tokens through the parse. Some barely parsed templates at all. Consider:

template<class T> struct S {
  T f(T);  // (1)
};
template<class T> T S<T>::f(T p = 42) { return p; }  // (2)
S<int> s;  // Causes the "real" parsing of (1), but not (2).
int r = s.f();  // (3)

When resolving call (3), older compilers therefore often only had access to the instantiated declaration (1), while (2) was still not really parsed (just token-buffered). As a result, such compilers were unaware of the added default argument in (3).

Is suspect caution made the committee therefore decide to more generally disallow added default arguments in templates.

That limitation is probably less (technically) justified today, since other standard requirements have since resulted in the need to parse templates in their generic form (though, e.g., MSVC still doesn't do so AFAICT). That said, it might still be a bit of a pain to implement, because default arguments would now potentially have to be instantiated in various different contexts.

Daveed V.
  • 946
  • 8
  • 8
  • V: what is the meaning of (2) just token buffered? – Destructor Sep 04 '15 at 17:20
  • 5
    Can't believe this question has been answered by template expert & author of C++ templates the complete guide: David Vandevoorde – Destructor Sep 04 '15 at 17:23
  • 2
    Re. "token buffered"... Some compilers (MSVC, EDG-based compilers, pre-3.4 GCC, and most older compilers like Cfront) handle templates by "buffering" the tokens they're composed of. Then at instantiation time, those tokens are "replayed" through the parser, except that template parameter tokens are replaced by the corresponding template argument. So a template could be in a "just token buffered; not parsed" state. – Daveed V. Sep 04 '15 at 17:33
  • There's something I don't quite understand, even assuming those older compilers... So, when parsing the call at (3), how does the compiler figure out which one of the hundreds of not-yet-parsed method definitions like (2) it must parse/instantiate now in order to get `S::f(int)`? Once the definition of class `S` is parsed, shouldn't it have some kind of table that maps member declarations like (1) to definitions like (2)? I think it should, if only to put together a list of viable candidates for the call. If so, then collecting default arguments from those candidates seems easy, no? – Igor G Mar 12 '20 at 19:41
-7

Because it's fundamentally impossible.

In order to achieve your goal, the compiler has to implement a function that, given two template functions, returns whether or not they are the same function. The problem is that this function is unimplementable.

For regular non-template functions, this is a terrible idea but still doable because you just have to match the argument types and job done.

Unfortunately, for template functions, this becomes... more tricky. Consider:

template<typename T> void f(T t);
template<typename U> std::enable_if_t<std::is_same<U, int>::value> f(U u = U());

You can see that they probably declare the same function, if T is int. Otherwise, they do not. There are more problematic interactions with default template arguments and similar things, but long story short, this would be undecidable for templates.

Puppy
  • 144,682
  • 38
  • 256
  • 465
  • 8
    The compiler *already* has to do declaration matching; you are totally allowed to declare the same function template multiple times. – T.C. Sep 02 '15 at 17:35
  • @T.C.: That doesn't actually require matching the declaration. – Puppy Sep 02 '15 at 17:46
  • *"There are more problematic interactions with default template arguments and similar things"* AFAIK, you can add default template arguments to a later re-declaration of a function template. Is this what you're alluding to? If so, why is this case different? – dyp Sep 02 '15 at 18:28
  • 1
    If you declare a function template several times, wouldn't the declaration have to be matched in order for overload resolution not to produce ambiguities? – dyp Sep 02 '15 at 18:29
  • Doesn't this function need to be implemented because of (and according to) [temp.over.link]? Esp., the non-normative p7 – dyp Sep 02 '15 at 18:35
  • @dyp: No, only specific concrete instantiations have to match, not the entire function template. – Ben Voigt Sep 03 '15 at 04:47
  • Your example does more than just add a default argument to the templated function. – davmac Jun 19 '16 at 11:36