Search code examples
c++structoperator-overloading

Overload an operator of a struct inside a template struct


I have a template struct Foo that defines an inner struct Bar.

Now, I would like to overload the stream operator << for this inner struct Bar, but the compiler seems to ignore my overload implementation:

error: no match for ‘operator<<’ (operand types are ‘std::ostream’ {aka ‘std::basic_ostream<char>’} and ‘Foo<3>::Bar’)

My code is the following:

#include <iostream>

//////////////////////////////////////////////////////////////////////////////////////////
template<int N>
struct Foo
{
    struct Bar  {};
};

//////////////////////////////////////////////////////////////////////////////////////////
template<int N>
std::ostream& operator<< (std::ostream& os, const typename Foo<N>::Bar& x)
{
    return os;
}

//////////////////////////////////////////////////////////////////////////////////////////
int main (int argc, char** argv)
{
    Foo<3>::Bar x;

    std::cout << x << std::endl;
}

I can't see the (maybe obvious) error.

Question Is it possible to overload an operator of an inner structure that belongs to a template class?


Solution

  • Yes, like this:

    #include <iostream>
    
    template<int N>
    struct Foo
    {
        struct Bar  {
            friend std::ostream& operator<<(std::ostream& os,const Bar&) { return os;}
        };
    };
    
    int main (int argc, char** argv)
    {
        Foo<3>::Bar x;
    
        std::cout << x << std::endl;
    }
    

    In your code the operator<< is not actually wrong. Its just impossible to deduce N from Foo<N>::Bar. You can only call it by making N explicit:

    int main (int argc, char** argv)
    {
        Foo<3>::Bar x;
        operator<<<3>(std::cout,x);
    }
    

    Live Demo.


    The reason is that Foo<N>::Bar is a non deduced context. See What is a non-deduced context? for more details. In a nutshell, the reason why this does not work is that there is no 1:1 relation between Foo<N>::Bar and N. Consider you add a specialization:

    template <> struct Foo<42> {
          using Bar = Foo<3>::Bar;
    };
    

    Now Foo<42>::Bar and Foo<3>::Bar refer to exactly the same type. This

    Foo<3>::Bar x;
    std::cout << x;
    

    and this

    Foo<42>::Bar x;
    std::cout << x;
    

    would have to deduce different values for N but in both examples x is of exactly the same type.