5

I have the following setup:

template< class T >
struct Foo {

  struct Bar {
    Bar ( const T &t ) : otherT_( t ) {}

    T otherT_;
  };

  Foo ( const T &t ) : myT_( t ) {}

  T myT_;
};

Now, I want to make instances of Foo< T >::Bar streamable to std::cout and friends. I tried this:

template< class T >
std::ostream& operator<< ( std::ostream &os, 
                           const typename Foo< T >::Bar &bar ) {
  os << "<bar: " << bar.otherT_ << ">";
  return os;
}

But the following code does not compile:

  Foo< int > foo( 5 );
  Foo< int >::Bar bar( 7 );

  std::cout << bar << std::endl;

I guess that the compiler is not able to deduce the type T or something. Is there a way to make such instances of the nested class behave well with operator<<?

Thank you!

TemplateRex
  • 69,038
  • 19
  • 164
  • 304
Sh4pe
  • 1,800
  • 1
  • 14
  • 30
  • 1
    Copy/paste [this answer](http://stackoverflow.com/a/18823636/596781)? – Kerrek SB Sep 16 '13 at 08:37
  • @KerrekSB So the answer is that it is not possible to simply write `std::cout << bar` ? Or can I make the one-to-one mapping mentioned in the linked answer explicit? – Sh4pe Sep 16 '13 at 08:39
  • 1
    @KerrekSB I'm not sure I follow the cited answer. The reason for his error is simple: the `T` in `Foo::Bar` is a non-deduced context, see §14.8.2.5/5. As others have pointed out, making the function a friend works (because the resulting function is not a template, so no type deduction is needed). – James Kanze Sep 16 '13 at 08:54
  • @JamesKanze: The "non-deduced context" is the common reason for both questions. It's always the problem of thinking that you can solve `Foo::Bar = X` for `T` given `X`. – Kerrek SB Sep 16 '13 at 09:26
  • @KerrekSB It's not a question of whether you can solve something, but rather the fact that the standard says you're not allowed to try. (The problem is, in fact, solvable, but was deemed to complex for the current compiler technology by the standards committee.) – James Kanze Sep 16 '13 at 09:39
  • @JamesKanze: Is it really solvable? There are in general many (or no) types `T` that satisfy the equation, with no obvious ordering among them...? – Kerrek SB Sep 16 '13 at 10:25
  • @KerrekSB Of course it's solvable, at least in this case: he's calling the function with a `Foo::Bar`, so there's not really an ambiguity. Where it becomes tricky is when `Bar` is a typedef. – James Kanze Sep 16 '13 at 19:59
  • @JamesKanze: OK, in this very strict case, fine. But consider `template <> struct Foo { typedef int Bar; };`. Suddenly you've broken `std::cout << 10;`... – Kerrek SB Sep 16 '13 at 20:56
  • @KerrekSB That's what I said. The problem for the compiler is with typedefs. The fact that `std::cout << 10` becomes ambiguous is secondary; this can happen whether templates are involved or not. The fact that the compiler might have to analyze the entire program to find the correct overload is more of an issue. – James Kanze Sep 17 '13 at 08:15

3 Answers3

10

Yep, the easy way is to put operator<< inside Bar:

struct Bar {
  Bar ( const T &t ) : otherT_( t ) {}

  T otherT_;

  friend std::ostream& operator<< ( std::ostream &os, const Bar &bar ) 
  {
    os << "<bar: " << bar.otherT_ << ">";
    return os;
  }
};

I am digging the other way ...

Raffi
  • 3,068
  • 31
  • 33
3

Workaround - define operator<< as a friend inside Bar's definition:

template< class T >
struct Foo {

  struct Bar {
    Bar ( const T &t ) : otherT_( t ) {}

    T otherT_;

    friend std::ostream& operator<< ( std::ostream &os, const Bar &bar )
    {
      os << "<bar: " << bar.otherT_ << ">";
      return os;
    }

  };

  Foo ( const T &t ) : myT_( t ) {}

  T myT_;
};

The problem with your approach is, as KerrekSB said in comments, that T could not be deduced. There are possibly infinitely many T for Foo<T>::Bar, each of which could result in a different type, too.

jrok
  • 54,456
  • 9
  • 109
  • 141
1

The compiler cannot deduce T, however, when you make it a friend it finds it via ADL.

I've modified the code to the following:

#include <iostream>
using namespace std;

template< class T >
struct Foo {

struct Bar {
Bar ( const T &t ) : otherT_( t ) {}

T otherT_;
friend std::ostream& operator << (std::ostream& os, const Bar &bar )
{ return os; }
};

Foo ( const T &t ) : myT_( t ) {}


T myT_;
};

int main() {
Foo< int > foo( 5 );
Foo< int >::Bar bar( 7 );

std::cout << bar << std::endl;
return 0;
}
Werner Erasmus
  • 3,988
  • 17
  • 31