52

Reading this question made me wonder: is there a technical reason for disallowing class templates overloads?

By overloading, I mean having several templates with the same names, but different parameters, for instance

template <typename T>
struct Foo {};

template <typename T1, typename T2>
struct Foo {};

template <unsigned int N>
struct Foo {};

The compiler manages to handle overloaded functions and function templates, wouldn't it be possible to apply the same techniques (e.g. name mangling) to class templates?

At first, I thought that perhaps that would cause some ambiguity issues when taking the template identifier alone, but the only time this can happen is when passing it as a template template argument, so the type of the parameter could be used to choose the appropriate overload:

template <template <typename> class T>
void A {};

template <template <unsigned int> class T>
void B {};

A<Foo> a; // resolves to Foo<T>
B<Foo> b; // resolves to Foo<N>

Do you think such feature could be useful? Is there some "good" (i.e. technical) reasons why this is not possible in current C++?

Community
  • 1
  • 1
Luc Touraille
  • 79,925
  • 15
  • 92
  • 137
  • maybe it will be ambiguous due to parameter deduction? – Gir Aug 15 '12 at 12:11
  • I've "needed" this (as in "I thought this would solve my problem") a couple of times before, but I always managed to do it some other way that wasn't too much of a hassle. Anyway, nice question. – R. Martinho Fernandes Aug 15 '12 at 12:19
  • 1
    @Gir: Could you elaborate? Are you refering to the template argument deduction that takes place when dealing with function templates? I must admit that I did not think thoroughly about the interactions there could be between template overloads and function overloads: that could be quite a mess. – Luc Touraille Aug 15 '12 at 12:19
  • yep, i was talking about deduction with function arguments. no idea what would happen – Gir Aug 15 '12 at 12:22
  • 1
    C++11 variadic templates feature is a very 'weak' answer to your question :) – iammilind Aug 15 '12 at 12:23
  • @R.MartinhoFernandes: I can think of several places where this could be/have been useful, especially in Boost.MPL. Variadic templates made the situation much more "bearable" though (no more need for endless lists of defaulted template parameters -- `mpl::na`, I'm looking at you). – Luc Touraille Aug 15 '12 at 12:31
  • All [numeric metafunctions](http://www.boost.org/doc/libs/1_49_0/libs/mpl/doc/refmanual/numeric-metafunction.html) could also be overloaded to accept both [integral constant wrappers](http://www.boost.org/doc/libs/1_49_0/libs/mpl/doc/refmanual/integral-constant.html) or plain integral constants. – Luc Touraille Aug 15 '12 at 12:40

3 Answers3

38

Section 12.5 from Templates the Complete Guide (Amazon) contains this quote:

You may legitimately wonder why only class templates can be partially specialized. The reasons are mostly historical. It is probably possible to define the same mechanism for function templates (see Chapter 13).

In some ways the effect of overloading function templates is similar, but there are also some subtle differences. These differences are mostly related to the fact that the primary template needs to be looked up when a use is encountered. The specializations are considered only afterward, to determine which implementation should be used.

In contrast, all overloaded function templates must be brought into an overload set by looking them up, and they may come from different namespaces or classes. This increases the likelihood of unintentionally overloading a template name somewhat.

Conversely, it is also imaginable to allow a form of overloading of class templates. Here is an example:

// invalid overloading of class templates
template<typename T1, typename T2> class Pair; 
template<int N1, int N2> class Pair; 

However, there doesn't seem to be a pressing need for such a mechanism.

Furthermore, the Design and Evolution of C++ (Amazon) contains this quote in section 15.10.3

I therefore concluded that we needed a mechanism for "specializing" templates. This could be done either by accepting general overloading or by some more specific mechanism. I chose a specific mechanism because I thought I was primarily addressing irregularities caused by irregularities in C and because suggestions of overloading invariably creates a howl of protests. I was trying to be cautious and conservative; I now consider that a mistake. Specialization as originally defined was a restricted and anomalous form of overloading that fitted poorly with the rest of the language.

Bold emphasis mine. I interpret this as saying that function overload resolution is more difficult to implement (and get right by users) than class specialization. So probably no real technical obstacles (similary for function template partial specialization) but an historical accident.

Rakete1111
  • 47,013
  • 16
  • 123
  • 162
TemplateRex
  • 69,038
  • 19
  • 164
  • 304
  • 2
    The quote from "Design and Evolution of C++" is interesting, because it shows that specialization was at first considered to be a weaker form of overloading, while they are in fact two very different things: overloading allows specifying multiple implementations that are selected based on the *type and number* of arguments used, while specialization is more related to [pattern matching](http://en.wikipedia.org/wiki/Pattern_matching) in functional programming, where implementations are selected based on the *values* of arguments. – Luc Touraille Aug 15 '12 at 12:48
  • @LucTouraille Is there anything that needs to be added to this answer to make it acceptable to you? – TemplateRex Sep 05 '12 at 11:49
  • No, it's perfect as it is :). It's just that I usually wait some time before accepting answers, in case new answers arrive a bit late. But then I often forget to come back and accept one, so I do "acceptance campaigns" every once in a while, where I look at all my questions with no accepted answers, and choose the best one; obviously, I would have chosen yours at this point :). – Luc Touraille Sep 05 '12 at 13:14
  • @LucTouraille Yeah, I know the backlog campaigns. Thanks anyway for the accept. Always nice to get an Enlightened medal out of answers :-) – TemplateRex Sep 05 '12 at 21:09
21

You cannot "overload" type parameter, non-type argument and template parameter, but you can specialize variadic template:

template <typename... T>
struct Foo;

template <typename T1>
struct Foo<T1> {};

template <typename T1, typename T2>
struct Foo<T1,T2> {};
chaohuang
  • 3,965
  • 4
  • 27
  • 35
log0
  • 10,489
  • 4
  • 28
  • 62
  • 3
    +1 Although the decision to not allow template overloads (I don't like that name, but by lack of a better one...) long predates the existence of variadic templates. Also note that this enables only a subset of template overloads, as all of the elements in the variadic part must be of the same *kind* (in this case types, you cannot provide an specialization for say `template struct Foo`. – David Rodríguez - dribeas Aug 15 '12 at 12:32
  • 2
    This is a nice workaround for having a variable number of homogeneous parameters. – Luc Touraille Aug 15 '12 at 12:50
2

This has been around for a while now, but I still found this post when searching. Thanks to @log0 for providing me with a good start. Here is a solution that avoids needing to provide a template specialisation for all possible enumerations. It does make one assumption: that you can define each template expansion in terms of itself and its base classes. (This would be done in FooImpl below):

template <typename... T>
struct Foo;

template<typename T>
struct Foo<T> { /* implementation of base class goes here*/};

template <typename C, typename Base>
struct FooImpl : public Base { /* implementation of derived class goes here */};

template<typename C, typename... Bases>
struct Foo<C, Bases...> : FooImpl<C, Foo<Bases...> > { /*NO IMPLEMENTATION HERE */};

The use of FooImpl breaks the ambiguous recursion that otherwise results. This then allows declarations such as the following:

Foo<int> foo_int;
Foo<int, double> foo_int_double;
Foo<int, float, double> foo_int_float_double;

Perhaps this is how the STL now does it?