views:

89

answers:

2

I have written the following code:

#include <iostream>
using namespace std;

template <class T>
class AA
{
  T a;

public:
AA()
{
 a = 7;
}

friend void print(const AA<T> & z);
};

template <class T>
void print(const AA<T> & z)
{
    cout<<"Print: "<<z.a<<endl;
}

void main()
{
AA<int> a;
print<int>(a);
}

And getting the following error:

error C2248: 'AA<T>::a' : cannot access private member declared in class 'AA<T>'
1>        with
1>        [
1>            T=int
1>        ]
1>        c:\users\narek\documents\visual studio 2008\projects\aaa\aaa\a.cpp(7) : see declaration of 'AA<T>::a'
1>        with
1>        [
1>            T=int
1>        ]
1>        c:\users\narek\documents\visual studio 2008\projects\aaa\aaa\a.cpp(30) : see reference to function template instantiation 'void print<int>(const AA<T> &)' being compiled
1>        with
1>        [
1>            T=int
1>        ]

What's wrong?

P.S. I am using Visual Studio 2008.

+2  A: 

The problem is that when you do something like

template<class T>
class AA {friend void print(const AA<T>&);};

and you instantiate AA with like this

AA<int> a;

the friend declaration will be instantiated like

friend void print(const AA<int>&);

which is a non-template function! This means the compiler will not match the friend declaration with your print function.

The solution is basically to declare print before AA and explicitly tell the compiler that your friend declaration is talking about a template function. Like this:

#include <iostream>
using namespace std;

//forward declare AA because print needs it
template<class T>
class AA;

//declare print before AA to make the friend declaration
//match with this function
template<class T>
void print(const AA<T> & z);

template <class T>
class AA
{
        //the <> is needed to make sure the compiler knows we're
        //dealing with a template function here
        friend void print<>(const AA<T> & z);

    public:
        AA() {a = 7;}

    private:
        T a;
};

//implement print
template<class T>
void print(const AA<T> & z)
{
    cout<<"Print: "<<z.a<<endl;
}

int main()
{
    AA<int> a;
    print(a);
}

It is interesting to see what happens if you do not add the <> in the friend declaration. You will get a linker error. Why? Well, because the compiler cannot match the friend declaration with your template print function, it will implicitly assume a function with prototype

void print(const AA<int>&);

exists. Since I didn't explicitly provide a template parameter to the call to print (which isn't necessary because the compiler should be able to deduce this), the compiler will match this call with the function declared as friend. This function is nowhere implemented, hence the linker error.

Job
It works. So strange...! If function is a friend function, that means that it is not a class function. How compiler can 'think' that function is a class's funcion? Or you ment something different by saying 'inside class'?
Narek
It turned out my explanation wasn't completely correct. Edited it and it should be more clear now:)
Job
Why I should declare print before AA? I have created frind functions to my class formerly, and implemented those functions in a .cc file (after class) and it work. Why now the fried function should be before AA? I mean how it 'helps' compiler?
Narek
You can also just forward declare the `print` function before `AA` and implement it somewhere else. I've edited my answer to show this.
Job
No the question is, why I should, at least, declare print before AA?
Narek
Because the compiler needs to know that you are talking about a template function. You do that by adding those `<>` in the friend declaration. But if you do that, the compiler needs to know that template function exists.
Job
+1  A: 

You can also define friend function inside class definition:

#include <iostream>
using namespace std;

template <class T>
class AA
{
    T a;
  public:
    AA()
    {
      a = 7;
    }

    friend void print(const AA<T> &z)
    {
      cout<<"Print: "<<z.a<<endl;
    }
};

int main()
{
  AA<int> a;
  print(a);
}
Tomek