c++templatescompiler-errorsfriendexplicit-instantiation

Explicit template instantiation of templated friend of templated class in C++


I have a main class MainClass whose private member variables should be visible to a friend class FriendClass. Both are templated by an int called dim, and they both have their respective header and source files (and are thus in different translation units). Since MainClass doesn't really depend on FriendClass (and to avoid circular dependency) I forward declare FriendClass as a template class when doing the friend declaration in MainClass. Additionally, at the end of the source files I explicitly instantiate both classes for dim = 2 and dim = 3. However, when I compile I get an error that the private member variable of MainClass is private within this context when I try to use it in a method of FriendClass. I suspect this has something to do with the fact that a particular instantiation of FriendClass does not recognize that the corresponding instantiation of MainClass has declared it a friend, but I'm not sure how to fix the problem. Code:

// MainClass.hpp
#ifndef MAIN_CLASS_HPP
#define MAIN_CLASS_HPP

template <int dim>
class MainClass
{
public:
    MainClass(){};
private:
    
    template <int friend_dim>
    class FriendClass;
    
    friend class FriendClass<dim>;
    
    double private_member = 3.0;
};

#endif
// MainClass.cpp
#include "MainClass.hpp"

template class MainClass<2>;
template class MainClass<3>;
// FriendClass.hpp
#ifndef FRIEND_CLASS_CPP
#define FRIEND_CLASS_CPP

#include "MainClass.hpp"

template <int dim>
class FriendClass
{
public:
    FriendClass(){};
    
    void print_main_class(MainClass<dim> &main_class);
};

#endif
// FriendClass.cpp
#include "FriendClass.hpp"

#include <iostream>

template <int dim>
void FriendClass<dim>::print_main_class(MainClass<dim> &main_class)
{
    std::cout << main_class.private_member << std::endl;
}

template class FriendClass<2>;
template class FriendClass<3>;
// main.cpp
#include "MainClass.hpp"
#include "FriendClass.hpp"

int main()
{
    const int dim = 2;
    MainClass<dim> main_class;
    FriendClass<dim> friend_class;
    
    friend_class.print_main_class(main_class);

    return 0;
}

Code available to compile live at onlinegdb.com


Solution

  • You have declared two different class templates both named FriendClass. Probably unintentionally.

    One is the global FriendClass and the other is MainClass::FriendClass.

    You can fix this by forward declaring your class template in the namespace it exists in.

    // MainClass.hpp
    #ifndef MAIN_CLASS_HPP
    #define MAIN_CLASS_HPP
    
    template <int>
    class FriendClass;
    
    template <int dim>
    class MainClass
    {
    public:
        MainClass(){};
    private:
        
        friend FriendClass<dim>;
    //  Now the global FriendClass is a friend.
        
        double private_member = 3.0;
    };
    
    #endif