Home > OS >  How do we declare a friend function with a class template into .h file and define them into a .cpp f
How do we declare a friend function with a class template into .h file and define them into a .cpp f

Time:04-20

When separating the declaration/definition of (a friend function a class template) an error occurs:
error LNK2001: unresolved external symbol "class std::basic_ostream<char,struct std::char_traits<char> > & __cdecl operator<<(class std::basic_ostream<char,struct std::char_traits<char> > &,class Property<int> const &)" (??6@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@std@@AAV01@ABV?$Property@H@@@Z)

Note that when using a friend function without a class template or vice versa everything works fine but when using the two things together an error occurs.

I tried to instantiate the template in the .cpp file but it didn't succeed.
Also, I have included the .cpp file at end of the .h file but it didn't work either.

class.h

template <class PropertyType>
class Property {
    PropertyType m_property;
public:
    const PropertyType& operator=(const PropertyType& value);
    friend std::ostream& operator<<(std::ostream& os, Property<PropertyType>& other);
}; 

class.cpp

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

template <class PropertyType>
std::ostream& operator<<(std::ostream& os, Property<PropertyType>& other) {
    os << other.m_property;
    return os;
}

template Property<int>;

main.cpp

int main() {
    Property<int> num;
    num = 100;
    std::cout << num << "\n";
}

What is wrong with them when separating the declaration/definition into two files and they are used together?

CodePudding user response:

There are two problem 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<<.

  • Related