Search code examples
c++operator-overloadingostream

C++ <<operator overloading with same type


I'm writing a method to print some spaces in std::cout, I know there are other ways using the standard library to achieve the same objective. Anyway, I used a typedef to store the number of spaces and an overloading of the << operator. But my overloading isn't called at all because my typedef is interpreted as an unsigned int.

So how can I tell to the compiler to call my function instead ?

class MyClass {
private: 
  typedef unsigned int space_type;

public: 
  std::ostream& operator<< (std::ostream& _os, space_type _n_spaces) {
    for (int _C = 0; _C < _n_spaces; _C++) 
      _os << " ";
    return _os;
  }

  void PrintNumbers(char _a, char _b) {
    space_type spaces = 5;
    std::cout << _a << spaces << _b << std::endl;
  }
}

int main () {
  MyClass class_instance;
  class_instance.PrintNumbers('K', 'X');

  std::cin.get();
  return 0;
}

This is the expected output:

K     X

This is the output I obtain:

K5X  // 5 is interpreted as an unsigned int, so my overloaded function 
     // isn't called, instead is called the std overloading with unsigned int

Solution

  • Since you're defining space_type as an alias (i.e. typedef) and not a type, it is not distinguishable from int, and the compiler will issue an error if you attempted to overload operator(std::ostream&, int).

    But what you're doing is defining a class member:

    std::ostream& operator<< (std::ostream& _os, space_type _n_spaces)
    

    When you define operators as class members, the first argument to the operator is (implicitly) an instance of the class. So in principle, that could only be called with:

    MyClass m;
    m << ???
    

    But here is a problem: an operator function called using infix notation can only have two parameters, and in the case of a member operator function, the first argument is implicit. m << x can only be implemented by MyClass::operator<<(decltype(x)).

    In short, you can only implement this with a non-member operator<< and the second argument to that overload must be a user type. So the following will work fine:

    struct space_t {
       unsigned x;
       space_t(unsigned x) : x(x) {}
       operator unsigned() const { return x; }
    };
    
    std::ostream& operator<< (std::ostream& os, space_t n) {
      for (unsigned i = 0; i < n; ++i) os << " ";
      return os;
    }
    

    See it on ideeone