Template friend function of a template class

我的未来我决定 提交于 2019-11-28 21:41:55
Richard Corden

...if I change the forward declaration of operator<< so that it doesn't match

A friend function should be seen as a very special type of declaration. In essence, the compiler does enough to parse the declaration however no semantic checking will take place unless you actually specialize the class.

After making your suggested modification, if you then instantiate test you will get an error about the declarations not matching:

template class test<int>;

...However ... removing the forward declaration causes a problem

The compiler tries to parse the declaration to store it until the class template is specialized. During the parse, the compiler reaches the < in the declaration:

friend std::ostream& operator<<  <

The only way that operator<< could be followed by < is if it is a template, so a lookup takes place to check that it is a template. If a function template is found, then the < is considered to be the start of template arguments.

When you remove the forward declaration, no template is found and operator<< is considered to be an object. (This is also why when you add using namespace std the code continues to compile as there must be declarations of templates for operator<<).

...when I remove the forward declarations and use the alternative friend declaration in the code above. Note that the template parameter U doesn't appear in the following signature...

There is no requirement that all template parameters be used in the arguments of a function template. The alternative declaration is for a new function template that will only be callable if declared in the namespace and specifying explicit template arguments.

A simple example of this would be:

class A {};
template <typename T> A & operator<<(A &, int);

void foo () {
  A a;
  operator<< <int> (a, 10);
}

...is this code actually correct?..

Well there are two parts to this. The first is that the alternative friend function does not refer to the declaration later in the scope:

template <typename T>
class test {
  template <typename U> 
  friend std::ostream& operator<<(std::ostream &out, const test<T> &t);
  };

template <typename T> 
std::ostream& operator<<(std::ostream &out, const test<T> &t);  // NOT FRIEND!

The friend function would actually be declared in the namespace for each specialization:

template <typename U> 
std::ostream& operator<<(std::ostream &out, const test<int> &t);
template <typename U> 
std::ostream& operator<<(std::ostream &out, const test<char> &t);
template <typename U>
std::ostream& operator<<(std::ostream &out, const test<float> &t);

Every specialization of operator<< <U> will have access to the specific specialization as per the type of its parameter test<T>. So in essence the access is restricted as you require. However as I mentioned before these functions are basically unusable as operators, since you must use function call syntax:

int main ()
{
  test<int> t;
  operator<< <int> (std << cout, t);
  operator<< <float> (std << cout, t);
  operator<< <char> (std << cout, t);
}

As per the answers to the previous question, you either use the forward declaration as suggested by litb, or you go with defining the friend function inline as per Dr_Asik's answer (which would probably be what I would do).

UPDATE: 2nd Comment

...changing the forward declaration before the class; the one in the class still matches the function that I implement later...

As I pointed out above, the compiler checks if operator<< is a template when it sees the < in the declaration:

friend std::ostream& operator<<  <

It does this by looking up the name and checking if it is a template. As long as you have a dummy forward declaration then this "tricks" the compiler into treating your friend as a template name and so the < is considered to be the start of a template argument list.

Later, when you instantiate the class, you do have a valid template to match. In essence, you're just tricking the compiler into treating the friend as a template specialization.

You can do this here because (as I said earlier), no semantic checking takes place at this point in time.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!