Why Is It Not Possible to Overload Class Templates

Why is it not possible to overload class templates?

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.

can't find overloaded method from inherited class template

class A_der : public A<B_der>
{
void init() override;
};

When you declare a function init in the derived class, it hides all things named init from the base class. This is just like when declaring something in an inner scope - it hides things with the same name from outer scopes.

There are ways to import the hidden names, but an easy solution would be to just chose a different name, like init_base. Or, probably better, pass a parameter to the class constructor.

When to prefer plain function overload over templates (overload)?

One big difference between template specializafions and overloads is that overloads allow implicit conversions of arguments, while template specializations do not. Continuing your example, how will float argument be handled? func(double param) will be chosen, but the generic template will be instantiated. Often the former is correct.

More generally, there are reasons to prefer non-template code. Templates must usually reside in a header, which slows down compilation and exposes implementation to everybody. Templates cause more cryptic compilation error messages and are not supported by tools very well. E.g. Visual studio 2015 does not give intellisense info for templates.

Overload C++ template class method by it's template classes

First off, you can't split template code into separate .h and .cpp files:

Why can templates only be implemented in the header file?

Second, a union's data is not set until runtime, so there is no way for the compiler to validate the template parameter of Get() at compile-time, at least not the way you want. It is possible to validate at compile-time only whether the union could never convert to the specified type at all, but if it could convert then you can't validate that until runtime, after the union has been assigned.

Try something like this:

#include <memory>
#include <type_traits>

template <class First, class Second>
class UnionPair {
static_assert(!std::is_same<First, Second>::value, "First and Second can't be the same type");

public:
UnionPair();
UnionPair(std::unique_ptr<First>&& value);
UnionPair(std::unique_ptr<Second>&& value);
~UnionPair(){}

void Reset();

void Set(std::unique_ptr<First>&& value);
void Set(std::unique_ptr<Second>&& value);

template <class T>
T* Get();

private:
enum class State { kEmpty, kFirst, kSecond } state_;

union {
std::unique_ptr<First> first_;
std::unique_ptr<Second> second_;
};

template<class T>
typename std::enable_if<std::is_same<T, First>::value, T*>::type
InternalGet();

template<class T>
typename std::enable_if<std::is_same<T, Second>::value, T*>::type
InternalGet();
};

template <class First, class Second>
UnionPair<First, Second>::UnionPair()
: state_(State::kEmpty)
{
}

template <class First, class Second>
UnionPair<First, Second>::UnionPair(std::unique_ptr<First>&& value)
: UnionPair()
{
Set(std::move(value));
}

template <class First, class Second>
UnionPair<First, Second>::UnionPair(std::unique_ptr<Second>&& value)
: UnionPair()
{
Set(std::move(value));
}

template <class First, class Second>
void UnionPair<First, Second>::Reset()
{
if (state_ == State::kFirst)
first_.reset();
else if (state_ == State::kSecond)
second_.reset();
state_ = State::kEmpty;
}

template <class First, class Second>
void UnionPair<First, Second>::Set(std::unique_ptr<First>&& value)
{
Reset();
first_ = std::move(value);
state_ = State::kFirst;
}

template <class First, class Second>
void UnionPair<First, Second>::Set(std::unique_ptr<Second>&& value)
{
Reset();
second_ = std::move(value);
state_ = State::kSecond;
}

template <class First, class Second>
template <class T>
typename std::enable_if<std::is_same<T, First>::value, T*>::type
UnionPair<First, Second>::InternalGet() {
if (state_ == State::kFirst)
return first_.get();
throw std::domain_error("wrong state");
}

template <class First, class Second>
template <class T>
typename std::enable_if<std::is_same<T, Second>::value, T*>::type
UnionPair<First, Second>::InternalGet() {
if (state_ == State::kSecond)
return second_.get();
throw std::domain_error("wrong state");
}

template <class First, class Second>
template <class T>
T* UnionPair<First, Second>::Get() {
return InternalGet<T>();
}
UnionPair<A, B> pair;
pair.Set(std::unique_ptr<A>(new A));
pair.Get<A>(); // OK
pair.Get<B>(); // runtime error
pair.Get<C>(); // compiler error

Why C++ doesn't allow template overloading?

It is extremely useful, but like you say, C++ doesn't allow you to do this directly. However, you can do almost the same thing with partial specialisation.

This is particularly easy if you use variadic templates in C++11, as you can do the following:

template <typename... T>
struct A; // Declared but not defined

template <typename T, typename U>
struct A<T, U>
{
// definition of the two-parameter case
};

template <typename T>
struct A<T>
{
// definition of the one-parameter case
};

Effectively, this allows you to have A<T, U> and A<T> as completely separate types. Attempting to instantiate an A with more template parameters will lead to a compile error as the general case is undefined (you could use a static_assert to give a nice error message if you wanted).

Something similar can be achieved in C++03 using default template parameters (set to an empty dummy struct, or void), but the C++11 version is much nicer IMO.

Why is it not possible to override operator for template classes involving third-party code?

The proper place to overload operators in in the namespace associated with the type.

For std::optional<std::unique_ptr<T>> there is one associated namespace std that is always there (from ostream and optional and unique_ptr), plus whatever namespace is associated with T. As you want to overload for all types, the namespace(s) associated with T are not useful to you.

It is not legal to introduce a new function or overload into std; in certain limited circumstances you can introduce specializations, but none of them apply here. Adding a new overload of << to std makes your program ill formed, no diagnostic required.

You could (A) use decorated unique_ptr or optionals from your own namespace, or (B) use a decorated ostream, or (C) write a formatter wrapper:

namespace fmt {
template<class T>
struct wrapper_t {
T&& t;
};
template<class T>
struct is_wrapped:std::false_type{};
template<class T>
struct is_wrapped<wrapper_t<T>>:std::true_type{};

template<class OS, class T,
std::enable_if_t<!is_wrapped<OS&>{}, bool> =true
>
auto operator<<( OS& os, wrapper_t<T>&& w )
-> decltype( os << std::forward<T>(w.t) )
{ return os << std::forward<T>(w.t); }
template<class OS, class T>
auto operator<<( wrapper_t<OS&> os, T&& t )
-> decltype( os.t << std::forward<T>(t) )
{ return os.t << std::forward<T>(t); }

template<class T>
wrapper_t<T> wrap(T&& t){ return {std::forward<T>(t)}; }
}

then std::cout << fmt::wrap( foo ) can find overloads of << within fmt, and if none are found invokes << on the contained data.

This also supports fmt::wrap(std::cout) instead of wrapping the arguments. There are probably typos.

Is it possible to overload a template class?

No. This is not allowed. Instead class template can be specialized (including partial specialization). This pretty much achieves the effect of overloading (which is only for functions)

Note that template parameters can not be deduced from constructor arguments.

template<class T> struct X{
void f(){}
};

template<class T> struct X<T*>{
void f(){}
};

int main(){
X<int> x;
x.f(); // calls X<T>::f

X<int *> xs;
xs.f(); // calls X<T*>::f
}

Operator overloading on class templates

// In MyClass.h
MyClass<T>& operator+=(const MyClass<T>& classObj);

// In MyClass.cpp
template <class T>
MyClass<T>& MyClass<T>::operator+=(const MyClass<T>& classObj) {
// ...
return *this;
}

This is invalid for templates. The full source code of the operator must be in all translation units that it is used in. This typically means that the code is inline in the header.

Edit: Technically, according to the Standard, it is possible to export templates, however very few compilers support it. In addition, you CAN also do the above if the template is explicitly instantiated in MyClass.cpp for all types that are T- but in reality, that normally defies the point of a template.

More edit: I read through your code, and it needs some work, for example overloading operator[]. In addition, typically, I would make the dimensions part of the template parameters, allowing for the failure of + or += to be caught at compile-time, and allowing the type to be meaningfully stack allocated. Your exception class also needs to derive from std::exception. However, none of those involve compile-time errors, they're just not great code.



Related Topics



Leave a reply



Submit