Three argument function template confusing example

class templates in c++ with simple example
template function c++
c++ function as template parameter
c++ template function specialization
templates in c++ pdf
c++ template <typename
c++ template function in class
example of template
#include <iostream>

// maximum of two values of any type:
template<typename T>
T max (T a, T b)
{
    std::cout << "max<T>() \n";
    return b < a ? a : b;
}

// maximum of three values of any type:
template<typename T>
T max (T a, T b, T c)
{
    return max (max(a,b), c); // uses the template version even for ints
} //because the following declaration comes
// too late:

// maximum of two int values:
int max (int a, int b)
{
    std::cout << "max(int,int) \n";
    return b < a ? a : b;
}

int main()
{
    ::max(47,11,33); // OOPS: uses max<T>() instead of max(int,int)
}

In this example (from book noted below) I didnt understand why ::max(47,11,33) call expected to use max(int,int). So one is 2 argument another is 3 argument i think it uses 3 argument function definition as it should.

Am I missing something?

Note: David Vandevoorde, Nicolai M. Josuttis, Douglas Gregor C++ Templates: The Complete Guide [2nd ed.] book

The issue that is being raised is that the non-template overload will not be called.

The max<T>(T a, T b, T c) knows about max<T>(T a, T b) but DOESN'T know that there's an integer overload because it's declared after it.

Templates - C++ Tutorials, In C++ this can be achieved using template parameters. For example, to create a template function that returns the greater one of two objects we could use: Confused by so many T's? There are three T's in this declaration: The first one is​  Fast forward to few years later, situation with using functions as template arguments much improved in C++11. You no longer bound to use Javaisms like functor classes, and can use for example static inline functions as template arguments directly.

A solution being: specialize max<T>for T = int rather than define a int(int, int) function:

#include <iostream>

template<typename T>
T max (T a, T b)
{
    std::cout << "max<T>() \n";
    return b < a ? a : b;
}

template<>
int max (int a, int b)
{
    std::cout << "max(int,int) \n";
    return b < a ? a : b;
}

template<typename T>
T max (T a, T b, T c)
{
    return max (max(a,b), c);
}

int main()
{
    ::max(47,11,33); // BINGO: uses specialized max<int>()
}

Output:

max(int,int) 
max(int,int) 

This is brittle though, if a non-template function come to use max<int> after max<T> has been defined and before max<int> has been specialized, as per [temp.expl.spec]/6 the program would be ill-formed, no diagnostic required.

If this is a risk you cannot take, there are tools available to you. SFINAE is one of them and could forbid the general max<T> to be called with T = int. This would lead to either a working program or a compilation error.

Templates, C++ FAQ, Here is a sample function template where the template parameter T does not confuse your users — you're probably better off using different functions with to deduce T . The third call, however, includes foo<T> with T = char and it wins. There is no way to explicitly specify template arguments to overloaded operators, conversion functions, and constructors, because they are called without the use of the function name. The specified template arguments must match the template parameters in kind (i.e., type for type, non-type for non-type,

::max(47, 11, 33); is in fact ::max<int>(47, 11, 33);

which in turns will call ::max<int>(::max<int>(47, 11), 33); which might be surprising.

As intis a built-in (so no ADL), max(int, int) should be visible before max(T, T, T) is defined to permit to call that version in template:

With custom type, thanks to ADL, your max function could be declared after:

template <typename T>
T max (T a, T b)
{
    std::cout << "max<T>()\n";
    return b < a ? a : b;
}

// Should be declared **before** max(T, T, T)
int max(int a, int b)
{
    std::cout << "max(int,int) \n";
    return b < a ? a : b;
}

template<typename T>
T max (T a, T b, T c)
{
    return max (max(a,b), c);
}

struct S {};

// Might be declared after max(T, T, T)
S max(S, S)
{
    std::cout << "max(S, S)\n";
    return {};
}

Now, both max(0, 1, 2) and max(s, s, s) would call internally the non template overload.

Demo

13.1, If the template function uses multiple template type parameter, they can template definition to create 3 different versions of this function: one  As we have seen how we could use template functions to not care about data type, we could use the same trick with C++ classes. If your C++ supports C11 standard, you can use variadic templates, which has the ability to use more parameters, something like combination of templates and functions of unknown number of arguments.

C++ Template Classes and Friend Function Details, Each behaves in a subtly different way, which causes all sorts of confusion when trying Template instantiation: A specific instance of a templated item; for example, if we 1 template<class T> 2 class A 3 { 4 public: 5 A(T a = 0): m_a(a) {} 6 7 C++ won't implicitly convert types for template function parameters, even if such  A template argument for a template template parameter must be an id-expression which names a class template or a template alias. When the argument is a class template, only the primary template is considered when matching the parameter.

Exploring C++ 11, The use of T as a template parameter name is merely a convention, but its The advantage of typename over class is that it avoids any confusion over nonclass types. T. For example, the copy algorithm is a function template with two parameters: This algorithm takes three arguments: the first two are input iterators that  Large() function returns the largest among the two arguments using a simple conditional operation. Inside the main() function, variables of three different data types: int, float and char are declared. The variables are then passed to the Large() function template as normal functions. During run-time,

Flask Web Development: Developing Web Applications with Python, Example 3-1 shows a Jinja2 template that matches the response of the index() view h1> The response returned by view function user() of Example 2-2 has a Any additional arguments are key/value pairs that represent actual values for in the previous example are fairly common but may seem confusing and hard to  function template with multiple parameter we will learn how to use multiple parameter in templates as well as All the Possible Mistake People do while using templates is discussed. This is a Must

Comments
  • Play around with the order in which you define your function. For example, what happens if you define (or at least declare) the int max(int, int) function before the three-argument template function? Or what if you use specialization instead of overloading? Like template<> int max(int, int);.
  • It's the calls to max(a,b) inside max(a,b,c) that use the template rather than the non-template overload.
  • i dont understand the question. the call does indeed call the 3 parameter function (which in turn calls the 2 parameter one twice).
  • @Mat thanks I noticed now.
  • @user463035818 yes, but the answer "you're missing reading the comments" doesn't answer the question quite as well
  • I thought ::max(47,11,33) expected to call two argment definition my confusing from last comment. Maybe "uses" misunderstood with "call"
  • @Kad but there are 2 argument definitions; one that's a template, and one that's integer only. The comments state // OOPS: uses max<T>() instead of max(int,int) showing that they're expecting to call the integer only one, but it doesn't
  • This is a consequence of two-phase lookup, correct?
  • @UKMonkey I know, I misunerstood first call of 3 argument function call as expected 2 argumented max(int,int).
  • @UKMonkey: "DOESN'T know that there's an integer overload because it's declared after it." it is more complicated than that, struct S{}; S max(S, S); max(S{}, S{}, S{}); would correctly call max(S, S) version even if declared after.
  • A major caveat worth mentioning is that the order of the specializations is brittle like this. This works because the 3 argument version is a template, so the point of instantiation for the 2 argument version being used is in main. But it's easy to add a function in between by accident, breaking the program.
  • @StoryTeller indeed.
  • It's worse than the non-specialized version being called. It's full on ill-formed NDR. Nasal demons are possible
  • No diagnostic required, damn!