6

I have the following template class and template function which intends to access the class' private data member:

#include <iostream>

template<class T>
class MyVar
{
    int x;
};

template<class T>
void printVar(const MyVar<T>& var)
{
    std::cout << var.x << std::endl;
}

template<class T>
void scanVar(MyVar<T>& var)
{
    std::cin >> var.x;
}

struct Foo {};

int main(void)
{
    MyVar<Foo> a;
    scanVar(a);
    printVar(a);
    return 0;
}

To declare the two functions as MyVar<T>'s friend functions, I've tried the following ways inside the declaration of template<class T> class MyVar to declare friendship. None of them works. How should I do?

template<class T> friend void printVar(const MyVar&);
template<class T> friend void scanVar(MyVar&);
// compilation error

template<class T> friend void printVar(const MyVar<T>&);
template<class T> friend void scanVar(MyVar<T>&);
// compilation error

friend void printVar(const MyVar<T>&);
friend void scanVar(MyVar<T>&);
// link error

friend void printVar(const MyVar&);
friend void scanVar(MyVar&);
// link error too
timrau
  • 22,578
  • 4
  • 51
  • 64
  • Did you try the last one with inline definition? [Like this](http://ideone.com/ThvvNp) – leemes May 25 '15 at 13:49
  • @leemes It works. Is inlined definition the only way to solve it? – timrau May 25 '15 at 13:53
  • Well, I checked again and found that your fourth solution seems to work, too... [See here](http://ideone.com/f0Fwc9) - What did you do differently? Note that the implementation of the functions need to be in the header file. Can it be the problem that it is then defined multiple times? What's the linker error? Inlining solves this "multiple definition" linker error, if this is what you got. You can also put the definition outside of the class but add the keyword `inline` in front of it to solve it. – leemes May 25 '15 at 13:56
  • @leemes The errors were indeed `undefined reference to `scanVar(MyVar&)'`, and adding `inline` in front of either the friend declarations or in front of the function definitions didn't work. – timrau May 25 '15 at 14:03
  • @leemes You can get the link error by completing `main()` definition like [here](http://ideone.com/aekAxk). – timrau May 25 '15 at 14:04

4 Answers4

6

The simplest option is to define the friend within the class:

template<class T>
class MyVar
{
    int x;

    friend void printVar(const MyVar & var) {
        std::cout << var.x << std::endl;
    }
    friend void scanVar(MyVar & var) {
        std::cin >> var.x;
    }
};

The downside is that the functions can only be called through argument-dependent lookup. That's not a problem in your example, but might be a problem if they don't have a suitable argument, or you want to specify the name without calling it.

If you want a separate definition, then the template will have to be declared before the class definition (so it's available for a friend declaration), but defined afterwards (so it can access the class members). The class will also have to be declared before the function. This is a bit messy, so I'll only show one of the two functions:

template <typename T> class MyVar;
template <typename T> void printVar(const MyVar<T> & var);

template<class T>
class MyVar
{
    int x;

    friend void printVar<T>(const MyVar<T> & var);
};

template <typename T> void printVar(const MyVar<T> & var) {
    std::cout << var.x << std::endl;
}
Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
2

I managed to get the following work

#include <iostream>

template<class T>
class MyVar;

template<class T>
void printVar(const MyVar<T>& var);

template<class T>
void scanVar(MyVar<T>& var);

template<class T>
class MyVar
{
    int x;
    friend void printVar<T>(const MyVar<T>& var);
    friend void scanVar<T>(MyVar<T>& var);
};

template<class T>
void printVar(const MyVar<T>& var)
{
    std::cout << var.x << std::endl;
}

template<class T>
void scanVar(MyVar<T>& var)
{
    std::cin >> var.x;
}

struct Foo {};

int main(void)
{
    MyVar<Foo> a;
    scanVar(a);
    printVar(a);
    return 0;
}

UPD: http://en.cppreference.com/w/cpp/language/friend talks about a similar case with operators under "Template friend operators":

A common use case for template friends is declaration of a non-member operator overload that acts on a class template, e.g. operator<<(std::ostream&, const Foo<T>&) for some user-defined Foo<T>

Such operator can be defined in the class body, which has the effect of generating a separate non-template operator<< for each T and makes that non-template operator<< a friend of its Foo<T>

...

or the function template has to be declared as a template before the class body, in which case the friend declaration within Foo<T> can refer to the full specialization of operator<< for its T

Petr
  • 9,812
  • 1
  • 28
  • 52
1

This one compiles on MSVC2013. Basicly adds the forward declarations to class and functions before the friend

template<class T>   class MyVar ; // class forward declaration

template<class T> ; // function forward declarations
void printVar(const MyVar<T>& var);
template<class T>
void scanVar(MyVar<T>& var);

template<class T>
class MyVar
{
    friend void printVar<T>(const MyVar<T>&);
    friend void scanVar<T>(MyVar<T>&);
    int x;
};

template<class T>
void printVar(const MyVar<T>& var)
{
    std::cout << var.x << std::endl;
}

template<class T>
void scanVar(MyVar<T>& var)
{
    std::cin >> var.x;
}

struct Foo {};

int main1(void)
{
    MyVar<Foo> a;
    scanVar(a);
    printVar(a);
    return 0;
}
marom
  • 5,064
  • 10
  • 14
1

Well there is a solution that is both simple and involving separation between declaration & definition of the friend function. In the declaration of the friend function (inside the class) you have to give a different template param from the one the class accepts (and it make sense cause this function is not a member of this class).

template<class T>
class MyVar
{
    int x;

    template<typename Type>
    friend void printVar(const MyVar<Type> & var);

    template<typename Type>
    friend void scanVar(MyVar<Type> & var);
};

template<typename T>
void printVar(const MyVar<T> & var) {

}

template<typename T>
void scanVar(MyVar<T> & var) {

}

No forward declaration needed, as well as there is a separation of declaration & definition.

Coral Kashri
  • 3,436
  • 2
  • 10
  • 22