Function Template Overloading

function template overloading

Of that list only the second introduces ambiguity, because functions - regardless of whether they are templates - can't be overloaded based on return type.

You can use the other two:

template<typename X> void func(X x, int y);

will be used if the second argument of the call is an int, e.g func("string", 10);

template<class X, class Y, class Z> void func(X x, Y y, Z z);

will be used if you call func with three arguments.


I don't understand why some other answers mentions that template functions and function overloading doesn't mix. They certainly do, and there are special rules how the function to call is selected.

14.5.5

A function template can be
overloaded with other function
templates and with normal
(non-template) functions. A normal
function is not related to a
function template (i.e., it is never
considered to be a specialization),
even if it has the same name and type
as a potentially generated function
template specialization.)

A non-templated (or "less templated") overload is preferred to templates, e.g

template <class T> void foo(T);
void foo(int);

foo(10); //calls void foo(int)
foo(10u); //calls void foo(T) with T = unsigned

Your first overload with one non-template parameter also falls under this rule.

Given choice between several templates, more specialized matches are preferred:

template <class T> void foo(T);
template <class T> void foo(T*);

int i;
int* p;
int arr[10];

foo(i); //calls first
foo(p); //calls second
foo(arr); //calls second: array decays to pointer

You can find a more formal description of all the rules in the same chapter of the standard (Function templates)


And finally there are some situations where two or more overloads would be ambiguous:

template <class T> void foo(T, int);
template <class T> void foo(int, T);

foo(1, 2);

Here the call is ambiguous, because both candidates are equally specialized.

You can disambiguate such situations with the use of (for example) boost::disable_if. For example, we can specify that when T = int, then the second overload shouldn't be included as an overload candidate:

#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/is_same.hpp>
template <class T>
void foo(T x, int i);

template <class T>
typename boost::disable_if<boost::is_same<int, T> >::type
foo(int i, T x);

foo(1, 2); //calls the first

Here the library produces a "substitution failure" in the return type of the second overload, if T = int, removing it from the set of overload candidates.

In practice you should rarely run into situations like that.

Overloading Function templates Rules

For the 1st overload, if T1 is specified as long double, and pass 7.2 which is a double, the implicit conversion from double to long double is required. For the 2nd overload, RT is specified as long double, and T1 will be deduced as double, so it's an exact match and wins in overload resolution.

If you specify template argument as double as ::max<double>(7.2, 4);, both of them are exact match and the call will be ambiguous.

Overload function template using a templated type

You have to write your own concept.

Adopted checking whether a class is a specialization from Check if class is a template specialization? and added a requires-clause to only accept the standard allocator.

template <class T, template <class...> class TT>
struct is_specialization : std::false_type {};

template <template <class...> class TT, class... Ts>
struct is_specialization<TT<Ts...>, TT> : std::true_type {};

template <class T, template <class...> class TT>
concept specializes = is_specialization<T, TT>::value;

template <typename T, specializes<std::vector> F>
T function(const F& f) const requires specializes<F::allocator_type, std::allocator>;

Unless the template-arguments may be provided manually, overload-resolution should work with the following overload though:

template <typename T, class V>
T function(const std::vector<V>& f) const;

Template specialization vs. Function overloading

Fist variant is simpler because compiler will have to collect and then choose from set of candidates containing only a single item add<int>(int, int) specialization. While second variant will cause compiler to perform some extra job by collecting a set of candidates containing two items - add<int>(int, int) and add(int, int) and choose between them using fancy function overload ranking algorithm.

Function template overloading vs Explicit specialization

Both are explicit specialization syntax

  • template <> double Sum(const map<string, double> &m);

  • template <> double Sum<map<string, double> >(const map<string, double> &m)

The first one let compiler deduce the parameter, whereas the second, you are explicit.

The second is required when compiler cannot deduced all template parameters as for

template <typename T> std::string getNameType();

template <> std::string getNameType<int>() { return "int"; }

or to disambiguate which template function to specialize

template <typename T> void foo(T);

template <typename T> void foo(T*); // overload, not specialization

//template <> void foo(int*); // Error: do you mean T = int for foo(T*) or T = int* for foo(T)

template <> void foo<int*>(int*); // specialize foo(T)
template <> void foo<int>(int*); // specialize foo(T*)

It is generally better to use overload instead of specialization for function, so for your example:

template <typename Key>
double Sum(const std::map<Key, double> &m)
{
double sum = 0;
for (const auto& p : m) { sum += p.second; } return sum;
}

Overloading Function Templates for C-strings

char const* max (char const* a, char const* b) returns an const char * variable by value. Then T const& max (T const& a, T const& b, T const& c) creates a temporary variable that stores that value and returns a const& reference to it (with T = const char *). That temporary pointer does not exist, when auto m2 = is assigned. Most probably, you print or inspect the value later, which causes some kind of "runtime error".

I guess, return a reference to const char * from the start.

char const * const& max (char const * const& a, char const * const& b) {
return std::strcmp(b, a) < 0 ? a : b;
}

C++ function template specialization and overloading

It prints "First" because the order of declaration affects which template you in fact specialize.

Your example has two function templates, which overload the same name. In the first case, you specialize void f(T p), because it's the only template seen so far.

In the second case, it's void f(T* p) that's specialized. So yes, your guess is correct. The specifics are at [temp.deduct.decl/1]:

In a declaration whose declarator-id refers to a specialization of a
function template, template argument deduction is performed to
identify the specialization to which the declaration refers
.
Specifically, this is done for explicit instantiations, explicit
specializations,
and certain friend declarations. [...]

And that includes the partial ordering of the function templates. However, partial ordering only applies to the available function template declarations at the point you introduce your specialization.

And the standard warns at [temp.expl.spec/7]:

The placement of explicit specialization declarations for function templates, [...] , can affect whether a program is well-formed
according to the relative positioning of the explicit specialization
declarations and their points of instantiation in the translation unit
as specified above and below. When writing a specialization, be
careful about its location; or to make it compile will be such a trial
as to kindle its self-immolation.

Passing overloaded function and args to template function

The reason for compilation error is that the compiler does not know which fun overload you are actually going to use.

To resolve this error, you just need to cast your function parameter to the right overload like:

int main()
{
call( static_cast< void(*)(int, int, int) >( fun ), 1, 2, 3 );

call( static_cast< void(*)(Demo&&, Demo&&) >( fun ), Demo{}, Demo{} );

return 0;
}

FYI, what your call function is trying to do is actually defined by the standard. It is std::invoke function and it comes with C++17 standard.



Related Topics



Leave a reply



Submit