Cubi73
Cubi73

Reputation: 1951

Additional template arguments for friend declaration in template class?

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>.)

Upvotes: 0

Views: 51

Answers (1)

Some programmer dude
Some programmer dude

Reputation: 409364

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).

Upvotes: 1

Related Questions