1

How can I get this to work? I want to separate the definition and declaration of the friend ostream funciton here.

#include <bits/stdc++.h>
using namespace std;

template <class T>
struct Outer {
    struct Inner {
        T x;
    };
    friend std::ostream& operator<<(std::ostream&, const Inner&);
};

template <class T>
std::ostream& operator<<(std::ostream& os, const Outer<T>::Inner& i) {
    os << "x! " << i.x;
    return os;
}

int main() {
    const Outer<int>::Inner s = {42};
    std::cout << s << "\n";
    return 0;
}

Adding typename as suggested by the error doesn't work too

template <class T>
std::ostream& operator<<(std::ostream& os, const typename Outer<T>::Inner& i) {
    os << "x! " << i.x;
    return os;
}
warning: friend declaration 'std::ostream& operator<<(std::ostream&, const Outer<T>::Inner&)' declares a non-template function [-Wnon-template-friend]
   10 |     friend std::ostream& operator<<(std::ostream&, const Inner&);
      |                                                                ^
<source>:10:64: note: (if this is not what you intended, make sure the function template has already been declared and add '<>' after the function name here)
/opt/compiler-explorer/gcc-13.2.0/bin/../lib/gcc/x86_64-linux-gnu/13.2.0/../../../../x86_64-linux-gnu/bin/ld: /tmp/ccBSBhtj.o: in function `main':
<source>:24: undefined reference to `operator<<(std::ostream&, Outer<int>::Inner const&)'
collect2: error: ld returned 1 exit status
Happytreat
  • 139
  • 7
  • 2
    (1) Get rid of `#include ` and use the relevant **standard** headers. (2) Try to avoid `using namespace std;`. – Adrian Mole Aug 29 '23 at 13:59
  • https://stackoverflow.com/a/8967610/4165552 - dupe? – pptaszni Aug 29 '23 at 14:02
  • Your 'fixed' code (second snippet) works for me in MSVC. What compiler/version are you using? – Adrian Mole Aug 29 '23 at 14:06
  • But I guess that's a **linker** error, and we don't have sufficient code to generate that. What does your 'minimal' `main` look like? – Adrian Mole Aug 29 '23 at 14:07
  • Whichever C++ textbook taught you to use `` -- you should throw it away and get a different C++ textbook. If you copied that off some web site, without any explanation, don't visit that web site any more. If you saw this in some clown's Youtube video, unsubscribe from that channel, you're not learning proper C++. This is not a standard C++ header file, many C++ compilers don't have it and will not compile the shown code. – Sam Varshavchik Aug 29 '23 at 14:10
  • @Sam Apparently, there are major schools and colleges where students are taught to use that header. I'm sure you're aware of [the canonical post about it](https://stackoverflow.com/q/31816095/10871073). – Adrian Mole Aug 29 '23 at 14:13
  • Added the main entry point! – Happytreat Aug 29 '23 at 14:14
  • are you sure to want `<<` be a friend of `Outer` not of `Inner` ? Though, I always get confused with access of members when nested classes are involved, what you have might be ok already. Is this for ADL, or merely to access the member (which is public in the example, because it is simplified) ? – 463035818_is_not_an_ai Aug 29 '23 at 14:15
  • Well, with the `main` you've now added and the aforementioned fix, your code builds and runs just fine (that is, when I remove that ugly header and use a real one, instead). – Adrian Mole Aug 29 '23 at 14:30
  • @AdrianMole not here https://godbolt.org/z/bKfe985Ej – 463035818_is_not_an_ai Aug 29 '23 at 16:00

1 Answers1

2

With:

template <class T>
std::ostream& operator<<(std::ostream& os, const typename Outer<T>::Inner& i)

T is non deducible.

Separate declaration from definition would not be evident.

Simpler would be

template <class T>
struct Outer {
    struct Inner {
        T x;

        friend std::ostream& operator<<(std::ostream &os, const Inner &i) {
            return os << "x! " << i.x;
        }
    };
};
Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • is there a way to make T deducible? I am aware that the inline definition would work but I want to try to separate the declaration and definiton. – Happytreat Aug 29 '23 at 14:18
  • Alternative are more **tricks** than actual solution as templating `operator<<(std::ostream&, const T&) requires (is_inner_v)` with concept/SFINAE to restrict to above type. – Jarod42 Aug 29 '23 at 14:21
  • @Jarod42 I suspect the OP would be interested in an example of those tricks .... – Peter Aug 29 '23 at 14:58
  • @Jarod42 If it's possible to get the `is_inner_v` trait working, there's another old question trying to solve that very problem [here](https://stackoverflow.com/questions/76516746/template-by-nested-structs-in-c). – Ted Lyngmo Aug 29 '23 at 14:58
  • 1
    @Peter: Ted Lyngmo provides [link for that question](https://stackoverflow.com/questions/76516746/template-by-nested-structs-in-c/77003083). – Jarod42 Aug 29 '23 at 19:24
  • @Jarod42 I had that Q on my watchlist. That's why you got an upvote within seconds after you posted :-) – Ted Lyngmo Aug 29 '23 at 19:58