Search code examples
c++classtemplatesfriend

How to declare a class template as friend of other template class


I'm trying implement a tuple-like class. Everything works well. This is my code:

Tuple.hpp

#pragma once

template<typename Head, typename... Tail>
class Tuple : private Tuple<Tail...>
{
   using Base = Tuple<Tail...>;

public:
   Tuple(Head head, Tail... tail) : Base{ tail... }, m_head{ head }{};
   Head& GetHead()
   {
    return m_head;
   }

    Base* base()
    {
        return static_cast<Base*>(this);
    }
    
    const Base* base() const
    {
        return static_cast<const Base*>(this);
    }

private:
    Head m_head;
};

template<typename Head>
class Tuple<Head>
{
public:
    Tuple(Head head) : m_head{ head } {};

    Head& GetHead()
    {
        return m_head;
    }

private:
    Head m_head;
};

template<int N, typename... Tail>
struct select;

template<int N, typename Head, typename... Tail>
struct select<N, Head, Tail...> : select<N - 1, Tail...>
{};

template<typename Head, typename... Tail>
struct select<0, Head, Tail...>
{
    using type = Head;
};

template<int N, typename... Tail>
using Select = typename select<N, Tail...>::type;

template<int N, typename R>
struct TgetNth
{
    template<typename T>
    static R& get(T& t)
    {
        return TgetNth<N - 1, R>::get(*t.base());
    }
};

template<typename R>
struct TgetNth<0, R>
{
    template<typename T>
    static R& get(T& t)
    {
        return t.GetHead();
    }
};

template<int N, typename... Tail>
Select<N, Tail...> get(Tuple<Tail...>& t)
{
    return TgetNth<N, Select<N, Tail...>>::get(t);
}

main.cpp

#include <iostream>
#include "Tuple.hpp"

int main()
{
    Tuple<double, int, std::string, double> tup{ 1.1, 10, "Hello world", 2.2 };

    std::cout << get<0>(tup) << std::endl;
    std::cout << get<1>(tup) << std::endl;
    std::cout << get<2>(tup) << std::endl;
    std::cout << get<3>(tup) << std::endl;
    
    return 0;
}

As you can see, struct TgetNth uses public methods Head& Tuple::GetHead() and Base* Tuple::base(). My idea to make these methods private and declare struct TgetNth as a friend of Tuple. I tried to make it like this:

friend struct TgetNth<0, Select<0, Tail...>>;

But I get an error from the compiler:

error C2248: 'Tuple<double,int,std::string,double>::m_head': cannot access private member declared in class 'Tuple<double,int,std::string,double>'

I'll be honest. I don't have any ideas how to do it. The snag is the int N parameter of the template.


Solution

  • You can make the entire TgetNth template a friend by declaring the following in Tuple:

    template<int, typename>
    // or template <int N, typename R>, but names are unnecessary
    friend struct TgetNth;
    

    See live example at Compiler Explorer.