How do we declare a friend function with a class template into .h file and define them into a .cpp file (not all in one header file)?

There are two problems with your code snippet.

The first problem is that you’ve put the implementation in the source file instead of the header file. So to solve this just move the implementation into the header file.

The second problem is that even if you move the implementation into the source file the program will still not work(Demo). This is because the friend declaration that you currently have(for overloaded opearator<<), is for an ordinary(non-template) function. That is, in your original code operator<< for class template Property<> is not function template, but “ordinary” function instantiated with the class template if needed. It is what we call a templated entity.

But the definition that you’ve provided in the source file(.cpp) for the overloaded operator<< is for a function template and not for an oridnary function. Thus, for the statement std::cout << num << "\n"; the linker cannot find the definition/implementation corresponding to the ordinary overloaded operator<< for which you had the friend declaration.

There are two ways to solve this:

Method 1

Add a separate parameter clause in the friend declaration.

template <class PropertyType>
class Property {
    PropertyType m_property;
public:
    const PropertyType& operator=(const PropertyType& value);
    template<typename T>  //parameter cluase added here
//---------------------------------------------------vvvvv----------------------->const added here
    friend std::ostream& operator<<(std::ostream& os,const Property<T>& other);
};
template <class PropertyType>
//----------------------------------------vvvvv---------------------------------->const added here
std::ostream& operator<<(std::ostream& os,const Property<PropertyType>& other) {
    os << other.m_property;
    return os;
} 

Demo

Method 2

Here we provide forward declaration for the overloaded operator<<.

//forward declaration for class template Property
template<typename T> class Property;

//forward declaration for overloaded operator<< 
template<typename T> std::ostream& operator<<(std::ostream&,const Property<T>&);//note the const in the second parameter
template <class PropertyType>
class Property {
    PropertyType m_property;
public:
    const PropertyType& operator=(const PropertyType& value);
//---------------------------------vvvvvvvvvvvvvv---------------------------------> angle brackets used here
    friend std::ostream& operator<<<PropertyType>(std::ostream& os,const Property<PropertyType>& other);//also note the const in the second parameter
}; 
template <class PropertyType>
const PropertyType& Property<PropertyType>::operator=(const PropertyType& value) {
    m_property = value;
    return m_property;
}

template <class PropertyType>
//----------------------------------------vvvvv---------------------------------->const added here 
std::ostream& operator<<(std::ostream& os,const Property<PropertyType>& other) {
    os << other.m_property;
    return os;
}

Demo

Method 3

If you want to provide the implementation inside the source file instead of the header file, then you should add

template std::ostream& operator<<(std::ostream& os, Property<int>& other);

inside the source file in addition to adding a template parameter clause for the friend declaration as shown below:

class.h

#ifndef MYCLASS_H
#define MYCLASS_H
#include <iostream>
template <class PropertyType>
class Property {
    PropertyType m_property;
public:
    const PropertyType& operator=(const PropertyType& value);
    template<typename T>  //parameter clause added
//---------------------------------------------------vvvvv--------------------->const added here
    friend std::ostream& operator<<(std::ostream& os,const Property<T>& other);
}; 


#endif

class.cpp

#include "class.h"

template <class PropertyType>
const PropertyType& Property<PropertyType>::operator=(const PropertyType& value) {
    m_property = value;
    return m_property;
}

template<typename PropertyType>
//----------------------------------------vvvvv------->const added here
std::ostream& operator<<(std::ostream& os,const Property<PropertyType>& other) {
    os << other.m_property;
    return os;
}
template class Property<int>;
template std::ostream& operator<<(std::ostream& os,const Property<int>& other);


main.cpp


#include <iostream>

#include "class.h"
int main() {
    Property<int> num;
    num = 100;
    std::cout << num << "\n";
}

Demo

The changes that I made include:

  1. Added a separate template parameter clause for the friend declaration. This is so that the friend declaration is for a function template. Moreover, we specify a different type parameter named T and not PropertyType because otherwise the new PropertyTypewill shadow the outerPropertyType`.

  2. Added a low-level const to the second parameter of the overloaded opeartor<<.

  3. In method3, inside source file(class.cpp), added

template std::ostream& operator<<(std::ostream& os,const Property<int>& other);

for the non-member function overloaded operator<<.

Leave a Comment