Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Defining the << operator of an inner class

Working on a project I did not initiate, I want to add an << operator to a class. Problem: the class is a private inner class of an other class, the latter being in a namespace.

And I cannot make it.

The problem can be simplified this way:

#include <iostream>
#include <map>
namespace A {
    class B {
        private:
            typedef std::map<int, int> C;
            C a;
            friend std::ostream& operator<<(std::ostream& os, const C &c) {
                for (C::const_iterator p = c.begin(); p != c.end(); ++p)
                    os << (p->first) << "->" << (p->second) << " ";
                return os;
            }
        public:
            B() {
                a[13] = 10;
                std::cout << a << std::endl;
            }
        };
}
int main() {
    A::B c;
}

I try to compile it with g++ test.cpp: error: no match for ‘operator<<’. The compiler did not find my overloaded function. I thought it would have been simpler to define it in the header, with no luck. If you think it is more appropriate, I could also define the class in the CPP file, but I do not know how to do.

Last requirement, I cannot use C++11 (unfortunately).

like image 702
unamourdeswann Avatar asked Jan 14 '14 08:01

unamourdeswann


People also ask

Can we overload << operator?

We can overload the '>>' and '<<' operators to take input in a linked list and print the element in the linked list in C++. It has the ability to provide the operators with a special meaning for a data type, this ability is known as Operator Overloading.

Can you forward declare a nested class?

You cannot forward declare a nested structure outside the container. You can only forward declare it within the container. Create a common base class that can be both used in the function and implemented by the nested class.

What is inner class in C++?

In object-oriented programming (OOP), an inner class or nested class is a class declared entirely within the body of another class or interface. It is distinguished from a subclass.


2 Answers

Since the friend operator is first declared inside the class, it's only available by argument-dependent lookup. However, neither of its parameter types are in namespace A, so it won't be found. C is an alias for std::map, so is considered to be in namespace std for the purposes of ADL.

There are various ways you could fix it, none of which are perfect:

  • Declare the function in namespace A before the class definition; then it becomes available by normal lookup, not just ADL. However, this breaks the encapsulation somewhat, and might cause problems if anything else tries to overload operator<< for std::map.
  • Replace the operator overload with a named static (not friend) function, and call it by name.
  • Declare C as an inner class, rather than an alias for std::map. This enables ADL without breaking encapsulation, but is a bit awkward if you want it to behave just like std::map.
like image 144
Mike Seymour Avatar answered Sep 22 '22 14:09

Mike Seymour


Based on Mike Seymour's answer, here's an example for the first solution. Note operator<<() should be defined outside of class B, and B::C's real type is exposed. It's not perfect but readable...

namespace A {

  // It has to expose the B::C's type
  std::ostream& operator<<(std::ostream& os, const std::map<int, int> &c);

  class B {
  private:
    typedef std::map<int, int> C;
    C a;
    friend std::ostream& operator<<(std::ostream& os, const B::C &c);
  public:
      B() {
        a[13] = 10;
        std::cout << a << std::endl;
      }
    };

  std::ostream& operator<<(std::ostream& os, const B::C &c) {
    for (B::C::const_iterator p = c.begin(); p != c.end(); ++p) {
      os << (p->first) << "->" << (p->second) << " ";
    }
    return os;
  }
}
like image 43
Mine Avatar answered Sep 25 '22 14:09

Mine