0

In order for some global function

template<typename T, int count>
void func (const Obj<T>& obj) {
    for (int i = 0; i < count; i++)
        std::cout << obj.value << std::endl;
}

to be able to access to the private field value of some templated class

template<typename T>
class Obj {
    public:
        Obj (T value);
    private:
        T value;
};

template<typename T>
Obj<T>::Obj (T value) : value(value) {}

we need to declare func<T, count> a friend of Obj<T>. But func<T, count> has to be declared before we can make it a friend of Obj<T>, and for this we need to forward-declare Obj<T>. The resulting code looks like this

// Forward declarations
template<typename T>
class Obj;

template<typename T, int count>
void func (const Obj<T>& obj);

// Obj<T>
template<typename T>
class Obj {
    public:
        Obj (T value);

        template<int count>
        friend void func<T, count> (const Obj<T>& obj);
    private:
        T value;
};

template<typename T>
Obj<T>::Obj (T value) : value(value) {} // <-- ERROR

// func<T>
template<typename T, int count>
void func (const Obj<T>& obj) {
    for (int i = 0; i < count; i++)
        std::cout << obj.value << std::endl;
}

But this makes gcc complain about the "invalid use of template-id 'func' in declaration of primary template", so how do I actually declare func<T, count> a friend of Obj<T> for every count? According to this answer I just need to replace the friend declaration with this

template<typename T1, int count>
friend void func (const Obj<T1>& obj);

As far as I know this would make func<T1, count> a friend of Obj<T> regardless of whether T1 and T match, which is absurd. Is it possible to declare func<T, count> a friend of Obj<T> and no other Obj<T1>? (preferably in a way that keeps the definition of func<T, count> outside the definition of Obj<T>)

(I know I could just make count a real parameter, but the example above is just a simplification of my real code. In reality I'm trying to overload std::basic_ostream<CharT, Traits>& operator<< (std::basic_ostream<CharT, Traits>& stream, const Obj<T>& obj) for some class Obj<T> in a way that allows operator<< to access private fields of Obj<T>.)

Cubi73
  • 1,891
  • 3
  • 31
  • 52
  • You don't need any of the two forward declarations you have (the class `Obj` or the function `func`). Instead the `friend` declaration of the function will act as a forward declaration on its own. – Some programmer dude Sep 29 '19 at 11:26
  • Can't you just define the function in the class? – L. F. Sep 29 '19 at 11:26

1 Answers1

1

The friend declaration must match any possible forward declaration, and of course the definition, including template arguments.

That means you need e.g.

template<typename U, int count>
friend void func(const Obj<U>& obj);

It doesn't matter if the class template argument T and the function template argument U are different, as the calls will be made to the correct function anyway.

Example:

Obj<int> int_obj;
Obj<float> float_obj;

func<X>(int_obj);  // Will call void func<int, X>(int_obj)
func<X>(float_obj);  // Will call void func<float, X>(float_obj)

As an alternative, you can define the function inline in the class definition, and then you don't need to provide the T or U template arguments:

template<int count>
friend void func(const Obj<T>& obj)
{
    // Implementation...
}

And in neither case you should really have a forward declaration of func (as mentioned in my comment).

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • But how do you ensure that `func` is not a friend of `Obj`, if `T` and `U` do not match? To me this seems to declare `func` a friend of `Obj` or am I wrong? – Cubi73 Sep 29 '19 at 11:31
  • And how would this work with `std::basic_ostream& operator<< (std::basic_ostream& stream, const Obj& obj)`? – Cubi73 Sep 29 '19 at 11:33
  • @Cubi73 If `U` is `std::string` then the declaration becomes `friend void func(const Obj& obj);`. Yes it will be a friend of e.g. `Obj` but you can't pass an `Obj` object to the function, so does it really matter? – Some programmer dude Sep 29 '19 at 11:33
  • Yes, it does matter. If I can make `func(Obj obj)` a friend of only `Obj` when just omitting the additional `count` argument, there should be a way to do the same when including the `count` argument. – Cubi73 Sep 29 '19 at 11:36