Skip to main content
Formatting
Source Link
Niall
  • 30.8k
  • 10
  • 107
  • 155

Alternatives include allowing the friendship to extend to all instantiations of the templates;

template <typename T>
class Foo {
    template <typename T1>
    friend std::ostream& operator<<(std::ostream& os, const Foo<T1>& a);
    // ...
};

The implementation for the operator<< could then be done inline inside the class definition, or outside.include;

template <typename T>
class Foo {
    friend std::ostream& operator<<(std::ostream& os, const Foo& a)
    { /*...*/ }
    // ...
};
  • Allowing the friendship to extend to all instantiations of the templates, as follows;

      template <typename T>
      class Foo {
          template <typename T1>
          friend std::ostream& operator<<(std::ostream& os, const Foo<T1>& a);
          // ...
      };
    
  • Or, the implementation for the operator<< can be done inline inside the class definition;

      template <typename T>
      class Foo {
          friend std::ostream& operator<<(std::ostream& os, const Foo& a)
          { /*...*/ }
          // ...
      };
    

NoteNote, when the declaration of the operator (or function) only appears in the class, the name is not available for "normal" lookup, only for argument dependent lookup, from cppreference;

Alternatives include allowing the friendship to extend to all instantiations of the templates;

template <typename T>
class Foo {
    template <typename T1>
    friend std::ostream& operator<<(std::ostream& os, const Foo<T1>& a);
    // ...
};

The implementation for the operator<< could then be done inline inside the class definition, or outside.

template <typename T>
class Foo {
    friend std::ostream& operator<<(std::ostream& os, const Foo& a)
    { /*...*/ }
    // ...
};

Note, when the declaration of the operator (or function) only appears in the class, the name is not available for "normal" lookup, only for argument dependent lookup, from cppreference;

Alternatives include;

  • Allowing the friendship to extend to all instantiations of the templates, as follows;

      template <typename T>
      class Foo {
          template <typename T1>
          friend std::ostream& operator<<(std::ostream& os, const Foo<T1>& a);
          // ...
      };
    
  • Or, the implementation for the operator<< can be done inline inside the class definition;

      template <typename T>
      class Foo {
          friend std::ostream& operator<<(std::ostream& os, const Foo& a)
          { /*...*/ }
          // ...
      };
    

Note, when the declaration of the operator (or function) only appears in the class, the name is not available for "normal" lookup, only for argument dependent lookup, from cppreference;

Further alternatives
Source Link
Niall
  • 30.8k
  • 10
  • 107
  • 155
template <typename T>
class Foo {
    friend std::ostream& operator<<(std::ostream& os, const Foo& a)
    { /*...*/ }
    // ...
};

Note, when the declaration of the operator (or function) only appears in the class, the name is not available for "normal" lookup, only for argument dependent lookup, from cppreference;

A name first declared in a friend declaration within class or class template X becomes a member of the innermost enclosing namespace of X, but is not accessible for lookup (except argument-dependent lookup that considers X) unless a matching declaration at the namespace scope is provided...

There is further reading on thistemplate friends at cppreference and the C++ FAQ.

Code listing showing the techniques aboveCode listing showing the techniques above.

There is further reading on this at cppreference and the C++ FAQ.

Code listing showing the techniques above.

template <typename T>
class Foo {
    friend std::ostream& operator<<(std::ostream& os, const Foo& a)
    { /*...*/ }
    // ...
};

Note, when the declaration of the operator (or function) only appears in the class, the name is not available for "normal" lookup, only for argument dependent lookup, from cppreference;

A name first declared in a friend declaration within class or class template X becomes a member of the innermost enclosing namespace of X, but is not accessible for lookup (except argument-dependent lookup that considers X) unless a matching declaration at the namespace scope is provided...

There is further reading on template friends at cppreference and the C++ FAQ.

Code listing showing the techniques above.

Source Link
Niall
  • 30.8k
  • 10
  • 107
  • 155

##Befriending templates...

Given the code snippet of a template type with a friend operator (or function);

template <typename T>
class Foo {
    friend std::ostream& operator<< (std::ostream& os, const Foo<T>& a);
};

The operator<< is being declared as a non-template function. For every type T used with Foo, there needs to be a non-templated operator<<. For example, if there is a type Foo<int> declared, then there must be an operator implementation as follows;

std::ostream& operator<< (std::ostream& os, const Foo<int>& a) {/*...*/}

Since it is not implemented, the linker fails to find it and results in the error.

To correct this, you can declare a template operator before the Foo type and then declare as a friend, the appropriate instantiation. The syntax is a little awkward, but is looks as follows;

// forward declare the Foo
template <typename>
class Foo;

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

template <typename T>
class Foo {
    friend std::ostream& operator<< <>(std::ostream& os, const Foo<T>& a);
    // note the required <>        ^^^^
    // ...
};

template <typename T>
std::ostream& operator<<(std::ostream&, const Foo<T>&)
{
  // ... implement the operator
}

The above code limits the friendship of the operator to the corresponding instantiation of Foo, i.e. the operator<< <int> instantiation is limited to access the private members of the instantiation of Foo<int>.

Alternatives include allowing the friendship to extend to all instantiations of the templates;

template <typename T>
class Foo {
    template <typename T1>
    friend std::ostream& operator<<(std::ostream& os, const Foo<T1>& a);
    // ...
};

The implementation for the operator<< could then be done inline inside the class definition, or outside.

There is further reading on this at cppreference and the C++ FAQ.

Code listing showing the techniques above.


As a side note to the failing code sample; g++ warns about this as follows

warning: friend declaration 'std::ostream& operator<<(...)' declares a non-template function [-Wnon-template-friend]

note: (if this is not what you intended, make sure the function template has already been declared and add <> after the function name here)