4

How can I make the insertion (<<) and/or extraction (>>) operator overloaded in a template class WITHOUT making it inline. I would like to have the << or >> operator as a friend class. I know how to make it inline example of inline in a matrix class

friend ostream& operator<<(ostream& ostr, const Matrix<T>& inputMatrix)
{
   ...
   // create the ostr
   return ostr;
}

but I'd like to have the code outside of the templateclass definition.

g++ told me to add <> after the function name so I did but when I tried to instansiate a matrix of type SOMETYPE it gave me an error that it didn't know how to extract or insert for that type.

sbi
  • 219,715
  • 46
  • 258
  • 445
  • 1
    You should read http://www.parashift.com/c++-faq-lite/templates.html#faq-35.16. BTW, as a template function it will be implicitly inline anyway. – UncleBens Nov 09 '10 at 19:20
  • Next time, please use the `101010` button atop the edit pane to format code in your message. This, and much more is explained on the "How to Edit" floating window on the right of the edit pane. – sbi Nov 09 '10 at 19:27

3 Answers3

3

If you really want to define the operator externally and befriend only the operator instantiation that coincides in type with this template instantiation, the correct syntax is:

template <typename T> class test; // forward declare template class
template <typename T>              // forward declare the templated operator
std::ostream& operator<<( std::ostream&, test<T> const & );

template <typename T>
class test {                      // define the template
   friend std::ostream& operator<< <T>( std::ostream&, test<T> const & ); // befriend
};
template <typename T>              // define the operator 
std::ostream& operator<<( std::ostream& o, test<T> const & ) {
   return o;
}

In most cases it is not worth the hassle to pull the definition out of the class, considering that you still need to provide it in a header and the extra work required.

Also note that there are slight differences for the compiler regarding lookup. In the case where the function is inlined inside the class definition, the compiler will not find that function unless one of the arguments is actually of the type of the template, so it effectively reduces the visibility and the amount of work that the compiler has to do (if the templated operator<< is defined outside of the class, the compiler will find it as a candidate for overload resolution in all places where it finds a << b, only to discard it in all cases where the second argument is not a test<T> (and it will show the templated operator as a candidate in all error messages where it cannot match operator<<, which is a long enough list already).

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • can you please exmplain why one would have to foreward declare the templated operator? –  Nov 11 '10 at 11:09
  • @Silverrocker: The main reason is that the language was designed thinking on a single pass compiler (\*). The issue is that the `friend` statement is telling the compiler: *grant access to this particular instantiation of the template*. For that to make sense for the compiler, the template must have already been declared. It is similar to: `void foo() { bar(5); } template void bar( T ) {}`. You cannot use or refer to an specialization or instantiation of a class template before the generic template and specializations is declared. – David Rodríguez - dribeas Nov 11 '10 at 11:17
  • (*) This is a goal, not a reality, all compilers need to go over the input file more than once for other reasons. Somewhere I read that the one that performed less passes over the initial code had 3 passes --including the preprocessor. I cannot provide a link to the source of that statement, nor remember what compiler it was. – David Rodríguez - dribeas Nov 11 '10 at 11:20
2

Try something like:

template <typename T> class Matrix;
template <typename T> std::ostream& operator<<(std::ostream& ostr, const Matrix<T>& m);

template <Typename T>
class Matrix
{
    public:

        friend ostream& operator<< <T> (ostream& ostr, const Matrix<K>& inputMatrix);
};

// This must be in the same translation unit as the class definition!
template<typename T>
ostream& operator<<(ostream& ostr, const Matrix<T>& inputMatrix)
{
   // ...
   return ostr;
}

Translation unit reference

re-re-edited to addressed the comments made by aschepler and dribeas.

luke
  • 36,103
  • 8
  • 58
  • 81
  • I think you need `const Matrix&` in the function definition. (The declaration can use the injected class name `Matrix` to mean `Matrix`, but that's not visible at the definition.) – aschepler Nov 09 '10 at 19:34
  • @aschepler, that issue should be corrected. You're right about the second thing too- inside the class I could have said `Matrix` instead of `Matrix`. What's weird is having to use a different identifier other than `T` in the class declaration of the operator. Maybe it's a quirk of the version of GCC I used, or maybe it's a Visual Studio extension that allows you to do that. Does anyone know what the standard says? – luke Nov 09 '10 at 19:40
  • Your version grants friendship to all `operator<<(ostream&, const Matrix&)` functions. To grant friendship to just the "matching" instantiation: http://pastebin.com/my9fSrcJ – aschepler Nov 09 '10 at 19:52
  • Re: the Standard: 14.5.3p1: "A friend of a class or class template can be a function template or class template, a specialization of a function template, or class template, or an ordinary (nontemplate) function or class." The version with another `class K` parameter declares an entire function template to be a friend. The version at my pastebin link declares a specialization of a function template as a friend. – aschepler Nov 09 '10 at 20:05
  • I was going to write a comment here, but decided that it would be too long. The proper code to declare a single instantiation of the template as a friend is provided in my answer. – David Rodríguez - dribeas Nov 09 '10 at 20:47
1

Place the code in the header, outside the class definition. Or, place it in a .tcc file and include it at the bottom of the header.

wilhelmtell
  • 57,473
  • 20
  • 96
  • 131