1

Hello dear StackOverflowers!

I am having trouble with a template structure which comprises another (but non-template) structure.

Here's the thing:

  1. Structure B is non-template and is defined inside a template structure A.

  2. Strcure B is "protected" because it exists only for the purposes of structure A and no-one and nothing else shall use it outside of structure A.

  3. There's an overload of operator<< for structure B, so that objects of type B can be sent to the standard output "cout << B_type_object;" (and it's A's friend so that it can access B which is protected).

  4. The aforementioned printing of B objects is done only by methods defined in A (because of "2.").

  5. As long as both structures are non-template everything works like a charm.

  6. The moment A is a template I get an error message (provided in the code section).

Here is the working code (where A is not template):

#include <iostream>
#include <string>

struct A
{

protected:
    struct B
    {
        std::string something;

        B& operator= (const std::string&& rhs)
        {
            this->something = std::move(rhs);
            return *this;
        }
    };

    B B_object;

    friend std::ostream& operator<< (std::ostream& output, const typename A::B& ob);

public:
    void method ()
    {
        B_object = "This is text.";
        //No error here
        std::cout << B_object;
    }
};



std::ostream& operator<< (std::ostream& output, const typename A::B& ob)
{
    output << ob.something;
    return output;
}



int main(int argc, const char * argv[])
{
    A obj;
    obj.method();

    return 0;
}

This is the code which doesn't work

#include <iostream>
#include <string>

template <typename T>
struct A
{
    T variable;

protected:
    struct B
    {
        std::string something;

        B& operator= (const std::string&& rhs)
        {
            this->something = std::move(rhs);
            return *this;
        }
    };

    B B_object;

    template <typename X> friend std::ostream& operator<< (std::ostream& output, typename A/*<X>*/::B& ob);

public:
    void method ()
    {
        B_object = "This is text.";

        //ERROR: Invalid operands to binary expression ('ostream' (aka 'basic_ostream<char>') and 'A<int>::B')
        std::cout << B_object;
    }
};



template <typename X>
std::ostream& operator<< (std::ostream& output, typename A<X>::B& ob)
{
    output << ob.something;
    return output;
}



int main(int argc, const char * argv[])
{
    A<int> obj;
    obj.method();

    return 0;
}
BR41N-FCK
  • 766
  • 6
  • 17

1 Answers1

2

Just declare the operator inline in B, then it works:

...
struct B
{
    std::string something;

    B& operator= (const std::string&& rhs)
    {
        this->something = std::move(rhs);
        return *this;
    }

    friend std::ostream& operator<< (std::ostream& output, const typename A<T>::B& ob)
    {
        output << ob.something;
        return output;
    }
};
...

This has also the advantage that your are not friending any operator<< of any A<X>. In your example, the operator taking A<string>::B would also be a friend of A<int>::B. For a more in-depth explanation, see this answer: https://stackoverflow.com/a/4661372/36565

Community
  • 1
  • 1
gexicide
  • 38,535
  • 21
  • 92
  • 152
  • You have really helped - thank you! But in a way my question still holds. What would I have to do to have operator<< out of line and working? – BR41N-FCK May 28 '14 at 18:55
  • 1
    @BR41N-FCK: The problem is that your `operator<<` is not known at the position of the friend declaration. If you could forward declare it (like the third case in the linked answer), everything would be fine. However, in your case you *cannot* forward declare it since you cannot forward declare `A::B`. – gexicide May 28 '14 at 20:25