Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to overload free function for member type of template

I have a template class that defines some member types. It's similar to how std::map defines it's value_type based on it's own template arguments, but in my case the type is more complex, so it's defined as nested class.

Now for debugging I would like to define operator<< for that type. But the compiler tells me it can't deduce the template parameters of the outer template.

My real code is not contrived like the following example, but this contrived example demonstrates the approach I tried and how it fails:

#include <iostream>

template <typename Value> class Outer;

template <typename Value>
std::ostream &operator<<(std::ostream &, const typename Outer<Value>::Inner &);

template <typename Value>
class Outer {
  public:
    struct Inner {
    Value x;
    };

    void PrintSomething(Value v) {
    // the real program does something useful with the inner class, of course
    Inner inner = { v };
    std::cout << "Inner = " << inner << std::endl; // <---- THIS SAYS IT CAN'T FIND operator<<
    };
};

template <typename Value>
std::ostream &operator<<(std::ostream &s, const typename Outer<Value>::Inner &v) {
    return s << v.x;
}

int main() {
    Outer<int> o;
    o.PrintSomething(42);
    return 0;
}

This is complete sample to reproduce the problem. The compiler (I've tried 3 of them) says there is no overload of operator<< that would take second argument of type Outer<int>::Inner. When I try the same thing with different function that does not have other overloads, it instead says C2783: could not deduce template argument for 'identifier', gcc and clang keep saying there is no overload that takes second argument Outer<int>::Inner).

So is there a way to define operator<< taking Outer<Value>::Inner for any Value as it's right (so it can't be defined as member) argument?

Note: I need it to compile in several compilers and some of them don't have any C++11 features, so I need it to be C++03.

like image 532
Jan Hudec Avatar asked Mar 14 '13 14:03

Jan Hudec


1 Answers

What you have there is a so-called non-deducible context. How could Value ever be deduced? You can partially specialize class templates, making it practically impossible for the compiler to even try and test every possible instantiation (of which there are.. well, infinite).

There are two workarounds: Take Inner out of Outer, or make operator<< an inline friend. The latter is the usual way people go.

template<class T>
struct Outer{
  struct Inner{
    T value;
    friend std::ostream& operator<<(std::ostream& os, Inner const& v){
      return os << v.value:
    }
  };
  // ...
};
like image 128
Xeo Avatar answered Oct 13 '22 12:10

Xeo