4

Before you ask, yes the function is defined in the header of my template class.

Here's the relevant bits of Example.h:

template<class T, class J, const int X, const int Y>
class Example {
    public:
        friend std::ostream& operator<<(std::ostream& s, const Example<T,J,X,Y>& b);
}

template<class T, class J, const int X, const int Y>
std::ostream& operator<<(std::ostream& s, const Example<T,J,X,Y>& b) {
    // stuff
}

I'm calling it from main.cpp:

void foo(Example<A,B,5,5>& b) {
    std::cout << b;
}

int main() {
    Example<A,B,5,5> b = Example<A,B,5,5>();
    foo(b);
}

When compiling I get the following linker error:

Undefined symbols for architecture x86_64:
  "operator<<(std::__1::basic_ostream<char, std::__1::char_traits<char> >&, Example<A, B, 5, 5> const&)", referenced from:
      foo(Example<A, B, 5, 5>&) in main.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make[2]: *** [dist/Debug/GNU-MacOSX/consolehero] Error 1
make[1]: *** [.build-conf] Error 2
make: *** [.build-impl] Error 2

1 Answers1

9

Long story short:

warning: friend declaration β€˜std::ostream& operator<<
(std::ostream&, const Example<T, J, X, Y>&)’ declares a non-template function

Your declaration does not declare a template function, since at instantiation all types are already known. You later define it as a template function, and the linker gets terribly confused. The easiest way is to define the operator inline, inside the class, like

template<class T, class J, const int X, const int Y>
class Example {
    public:
        friend std::ostream& operator<<(std::ostream& s, const Example& b)
        { 
            // stuff here
        }
};

Or, use

template<class T, class J, const int X, const int Y>
class Example {
    public:
        template<class T1, class J1, const int X1, const int Y1>
        friend std::ostream& operator<<(std::ostream& s, 
                                         const Example<T1,J1,X1,Y1>& b);
};

template<class T1, class J1, const int X1, const int Y1>
std::ostream& operator<<(std::ostream& s, const Example<T1,J1,X1,Y1>& b) {
    // stuff
}

I find the first approach much more tempting. Note that the 2 approaches are NOT equivalent. In the first case, a non-template operator<< is being generated for every instantiation, whereas in the second case, the operator<< is templated, so it will be generated only when it is explicilty called with the type of its friended class, via type deduction. This is a rather subtle point, and I really don't see when one would prefer the more general second approach vs the first one.

PS: For first approach, see http://en.cppreference.com/w/cpp/language/friend Template friend operators, for more clarifications. Citing from it:

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

So, essentially, an in class definition like

friend std::ostream& operator<<(std::ostream& s, const Example& b){}

is equivalent to

friend std::ostream& operator<<(std::ostream& s, const Example<T,J,X,Y>& b){}
vsoftco
  • 55,410
  • 12
  • 139
  • 252
  • Man, this stuff is confusing. I got it to work by removing the definition in the class. Then I added `inline` to the definition. It works - but how? How the program know to use THAT function when calling the `<<` operator on Example? –  Nov 29 '14 at 21:06
  • @maxmackie found some documentation, see the updated edit or http://en.cppreference.com/w/cpp/language/friend (Template friend operators) – vsoftco Nov 29 '14 at 21:23