0

I've recently learned that there are two ways to declare a template friend class or function. For example, to declare a template friend class, you may do this

template <typename T>
class goo
{
    template <typename T>
    friend class foo;
};

or this

template <typename T>
class goo
{
    friend class foo <T>;
};

These two declarations are in fact different. The former allows you to use any type of template friend class foo with any type of template friend class goo. Where as the latter only allows you to use the same type such that you may do foo<int> with goo<int> but not foo<int> with goo<char>.

In the header file below, I try to use the latter form of the declaration to make my template friend function friend std::ostream& operator<<(std::ostream&, const Array<T>&); more type-specific in an effort to make my program more encapsulated.

//ARRAY_H

#include <iostream>
#include "Animal.h"

const int DefaultSize = 3;

template <typename T> // declare the template and the paramenter
class Array               // the class being parameterized
{
public:
  Array(int itsSize = DefaultSize);
  Array(const Array &rhs);
  ~Array() { delete[] pType; }

  // operators
  Array& operator=(const Array&);
  T& operator[](int offSet) { return pType[offSet]; }
  const T& operator[](int offSet) const { return pType[offSet]; }

  // accessors
  int GetSize() const { return itsSize; }

  // friend function
  friend std::ostream& operator<< <T>(std::ostream&, const Array<T>&);

private:
  T *pType;
  int itsSize;
};

template <typename T>
Array<T>::Array(int size = DefaultSize) :itsSize(size)
{
  pType = new T[size];
  for (int i = 0; i < size; i++)
      pType[i] = static_cast<T>(0);
}

Array<Animal>::Array(int AnimalArraySize) :itsSize(AnimalArraySize)
{
  pType = new Animal[AnimalArraySize];
}

template <typename T>
Array<T>::Array(const Array &rhs)
{
  itsSize = rhs.GetSzie();
  pType = new T[itsSize];
  for (int i = 0; i < itsSize; i++)
      pType[i] = rhs[i];
}

template <typename T>
Array<T>& Array<T>::operator=(const Array &rhs)
{
  if (this == &rhs)
      return *this;
  delete[] pType;
  itsSize = rhs.GetSize();
  pType = new T[itsSize];
  for (int i = 0; i < itsSize; i++)
      pType[i] = rhs[i];
  return *this;
}
template <typename T>
std::ostream& operator<<(std::ostream& output, const Array<T> &theArray)
{
  for (int i = 0; i < theArray.GetSize(); i++)
      output << "[" << i << "]" << theArray[i] << std::endl;
  return output;
}

#endif

However, I receive a compiler error "error C2143: syntax error : missing ';' before '<'" for line 23 which is friend std::ostream& operator<< <T>(std::ostream&, const Array<T>&);.

When using the former form of the declaration by changing line 23 to this

template <typename T>
friend std::ostream& operator<<(std::ostream&, const Array<T>&);

My program executes without any errors.

I assume I cannot use the same syntax from type-specific template friend classes for type-specific template friend functions, or that I may be missing some kind of forward declaration. I've searched through stack-overflow and the closest topic I could find for this problem is here, but they only discuss type-specific template friend classes. I am unable to find a topic that discusses the correct syntax for using a template friend function in this way.

If this is a syntax error, what is the correct way to declare my type-specific template friend function? If this is not a syntax error, why will my program not compile?

Here are the rest of my project files for your reference. The desired behavior of my program is to show how a parametrized array uses a template to create multiple instances of different array types.

//ANIMAL_H

#ifndef ANIMAL_H
#define ANIMAL_H

#include <iostream>

class Animal
{
public:
  // constructors
  Animal();
  Animal(int);
  ~Animal();

  // accessors
  int GetWeight() const { return itsWeight; }
  void SetWeight(int theWeight) { itsWeight = theWeight; }

  // friend operators
  friend std::ostream& operator<<(std::ostream&, const Animal&);

private:
  int itsWeight;
};

#endif

//ANIMAL.CPP

#include "Animal.h"
#include <iostream>

Animal::Animal() :itsWeight(0)
{
  std::cout << "animal() ";
}

Animal::Animal(int weight) : itsWeight(weight)
{
  std::cout << "animal(int) ";
}

Animal::~Animal()
{
  std::cout << "Destroyed an animal...";
}

std::ostream& operator<<(std::ostream& theStream, const Animal& theAnimal)
{
  theStream << theAnimal.GetWeight();
  return theStream;
}

//MAIN.CPP

#include <iostream>
#include "Animal.h"
#include "Array.h"

void IntFillFunction(Array<int>& theArray);
void AnimalFillFunction(Array<Animal>& theArray);

int main()
{
  Array<int> intArray;
  Array<Animal> animalArray;
  IntFillFunction(intArray);
  AnimalFillFunction(animalArray);
  std::cout << "intArray...\n" << intArray;
  std::cout << "\nanimalArray...\n" << animalArray << std::endl;

  std::cin.get();

  return 0;
}

void IntFillFunction(Array<int>& theArray)
{
  bool Stop = false;
  int offset, value;
  while (!Stop)
  {
      std::cout << "Enter an offset (0-9) and a value. ";
      std::cout << "(-1 to stop): ";
      std::cin >> offset >> value;
      if (offset < 0)
          break;
      if (offset > 9)
      {
          std::cout << "***Please use values between 0 and 9.***\n";
          continue;
      }
      theArray[offset] = value;
  }
}

void AnimalFillFunction(Array<Animal>& theArray)
{
  Animal *pAnimal;
  for (int i = 0; i < theArray.GetSize(); i++)
  {
      pAnimal = new Animal(i * 10);
      theArray[i] = *pAnimal;
      delete pAnimal;
  }
}
Community
  • 1
  • 1
Wandering Fool
  • 2,170
  • 3
  • 18
  • 48

2 Answers2

5

You need to declare the function template before you refer to a specialization as a friend.

// Forward declare class template.
template <typename T> class Array;

// Declare function template.
template <typename T>
std::ostream& operator<<(std::ostream& os, const Array<T>& arr);

template <typename T>
class Array
{
    //...
    friend std::ostream& operator<< <>(
        std::ostream& os, const Array<T>& arr);
    //...
};
aschepler
  • 70,891
  • 9
  • 107
  • 161
  • I notice that you use <> and the guy below uses . Both work, but I want to know what the specific difference between the two are no matter how insignificant. Help me understand. – Wandering Fool Apr 08 '15 at 01:04
0

You need a forward declaration of the function template (just as you need with a class template) in order to friend a specialization. Your code should be:

template <typename T>
std::ostream& operator<<(std::ostream& output, const Array<T> &theArray);

template <typename T>
class Animal
{
    // ...
    friend std::ostream& operator<< <T>(std::ostream&, const Array<T>&);
};
David G
  • 94,763
  • 41
  • 167
  • 253
  • I notice that you use and the guy above uses <>. Both work, but I want to know what the specific difference between the two are no matter how insignificant. Help me understand. – Wandering Fool Apr 08 '15 at 01:03
  • @WanderingIdiot In this context, there's no difference between `<>` and ``. But say you are befriending a function for which there are template and non-template overloads. Using `<>` selects the template version and without it it selects the non-template version. – David G Apr 08 '15 at 01:27
  • I'm having difficulty understanding what a function with template and non-template overloads would look like. Could you explain this through an example? – Wandering Fool Apr 08 '15 at 01:47
  • @WanderingIdiot Simple. If you have `template void f(T);` and `void f(int)` then `friend void f<>(int)` befriends the template overload with `T = int` whereas `friend void f(int)` befriends the non-template overload. – David G Apr 08 '15 at 02:10
  • I thought you were trying to explain a difference between `<>` and `` in a different context. But it seems to me your example works with both `<>` and ``. I can understand that for a template declaration such as `template class foo;` the `T` can be removed like this `template class foo;` because `T` is not being used at all in the declaration and therefore is not needed. I thought that there might be a deeper meaning behind not using `T` in `` such as in my example of template declarations. – Wandering Fool Apr 08 '15 at 02:25
  • @WanderingIdiot The `T` in `` isn't needed because it is deduced by the parameter. In your example `T` is deduced by the second parameter `const Array&`, so explicit specification of `T` isn't needed in the friend declaration. If `T` wasn't deduced then it would be required. My example was showing when you would use `<>` vs not using one. OTOH class template arguments aren't deduced, so you need to explicitly specify the template arguments in the friend declaration. But if you do so, then there needs to be a prior declaration of the class. – David G Apr 08 '15 at 02:30
  • I understand `<>` and `` now, but from what I understand of your statement on explicitly specifying the template arguments in the friend declaration for a template class, [this](http://warp.povusers.org/programming/template_declarations.html) says under **"Practical usage example 1: Friend template classes"** that I can declare a friend class without specifying type `T`. Maybe I'm misunderstanding your last remark. – Wandering Fool Apr 08 '15 at 03:50
  • @WanderingIdiot You can befriend a class template, or a *specialization* of a class template. If you befriend a class template then all specializations of that class are friends of the class in which it is declared a friend (like in your first example). If you befriend a specialization then only that class with those specific template arguments are friends of the class in which it is declared a friend (like in your second example) but there needs to be a prior declaration of the class so the compiler knows which specialization you're referring to. – David G Apr 08 '15 at 10:56