Overload operator<< for nested class template
Asked Answered
B

3

7

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!

Bollen answered 16/9, 2013 at 8:33 Comment(9)
Copy/paste this answer?Impoverish
@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?Bollen
@KerrekSB I'm not sure I follow the cited answer. The reason for his error is simple: the T in Foo<T>::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).Stig
@JamesKanze: The "non-deduced context" is the common reason for both questions. It's always the problem of thinking that you can solve Foo<T>::Bar = X for T given X.Impoverish
@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.)Stig
@JamesKanze: Is it really solvable? There are in general many (or no) types T that satisfy the equation, with no obvious ordering among them...?Impoverish
@KerrekSB Of course it's solvable, at least in this case: he's calling the function with a Foo<int>::Bar, so there's not really an ambiguity. Where it becomes tricky is when Bar is a typedef.Stig
@JamesKanze: OK, in this very strict case, fine. But consider template <> struct Foo<int> { typedef int Bar; };. Suddenly you've broken std::cout << 10;...Impoverish
@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.Stig
A
11

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 ...

Anzovin answered 16/9, 2013 at 8:42 Comment(2)
:) still trying the other way :)Anzovin
I thought template-aliases might help with the deduction, but this answer set me straightAdina
C
4

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.

Calisaya answered 16/9, 2013 at 8:42 Comment(0)
V
2

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;
}
Viera answered 16/9, 2013 at 8:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.