Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I call a friend template function defined inside a class?

Tags:

c++

templates

I got this example from my book, but I have no idea how to actually call the ticket function. This is the code:

#include <iostream>
    class Manager { 
    public:
        template<typename T> 
            friend int ticket() { 
                return ++Manager::counter; 
            } 
        static int counter; 
    }; 
   
int main()
{
    Manager m;
    std::cout << "ticket: " << ticket<int>() << std::endl;
}

I get the "candidate function(s) not accessible" error message.

like image 639
pyralis Avatar asked Nov 28 '22 19:11

pyralis


1 Answers

A few points will help you figure out what's going on here:

I) Friend function definitions within classes can only be found by Argument dependent lookup when called from outside the class definition.

II) Function templates that are supplied explicit template arguments do not undergo ADL unless the compiler is given some explicit help in identifying the call as a function call.

III) Argument dependent lookup (ADL) only works for user defined types.

A few examples will better illustrate each of the above points:

//------------------------
struct S 
{
  friend int f(int) { return 0; }  // 1
  friend int f(S) { return 0; }    // 2

};

S s;
int i = f(3); // error - ADL does not work for ints, (III) 
int j = f(s); // ok - ADL works for UDTs and helps find friend function - calls 2 (III)

// so how do we call function 1? If the compiler won't find the name via ADL
// declare the function in the namespace scope (since that is where the friend function
// gets injected)

int f(int);  // This function declaration refers to the same function as #1
int k = f(3); // ok - but not because of ADL 

// ok now lets add some friend templates and make this interesting
struct S 
{
  friend int f(int) { return 0; }  // 1
  friend int f(S) { return 0; }    // 2
  template<class T> friend int g(int) { return 0; } // 3
  template<class T> friend int g(S) { return 0; } // 4
  template<class T> friend int g() { return 0; } // 5
};
S s;
int k = g(5); // error - no ADL (III)
int l = g(s); // ok - ADL - calls 4
int m = g<int>(s); // should call 4 - but no ADL (point II above)

// ok so point II above says we have to give the compiler some help here
// We have to tell the compiler that g<int> identifies a function
// The way to do that is to add a visible dummy template function declaration

template<class /*Dummy*/, class /*TriggerADL*/> void g(); 

int m = g<int>(s); // ok - compiler recognizes fun call, ADL triggered - calls 4
int n = g<int>(3); // still not ok - no ADL for ints

// so how do we call either function 3 or 5 since we cannot rely on ADL?
// Remember friend functions are injected into the outer namespace
// so lets just declare the functions in the outer namespace (as we did above)
// both these declarations of g below refer to their counterparts defined in S
template<class T> int g(int);
template<class T> int g();
int o = g<int>(3); // ok
int p = g<int>(); // ok

// Of course once you have these two declarations at namespace scope
// you can get rid of the Dummy, TriggerADL declaration.

Ok so now lets return to the Vandevoorde example that you quoted, and now this should be easy:

#include <iostream>
class Manager { 
public:
    template<typename T> 
        friend int ticket() { 
            return ++Manager::counter; 
        } 
    static int counter; 
}; 

int Manager::counter;

template<class T> int ticket(); // <-- this should work with a conformant compiler  

int main()
{
  Manager m;
  std::cout << "ticket: " << ticket<int>() << std::endl;
}

Hope that helps :)

like image 139
Faisal Vali Avatar answered Dec 05 '22 09:12

Faisal Vali