4

I am trying to overload

<<

operator. For instance

cout << a << " " << b << " "; // I am not allowed to change this line

is given I have to print it in format

<literal_valueof_a><"\n>
<literal_valueof_b><"\n">
<"\n">

I tried to overload << operator giving string as argument but it is not working. So I guess literal

" "

is not a string. If it is not then what is it. And how to overload it? Kindly help;

Full code

//Begin Program
// Begin -> Non - Editable     
    #include <iostream>
    #include <string>
    using namespace std;

// End -> Non -Editable
//---------------------------------------------------------------------
// Begin -> Editable       (I have written )
    ostream& operator << (ostream& os, const string& str) {
        string s  = " ";
        if(str  ==  " ") {
            os << '\n';
        }
        else {
            for(int i = 0; i < str.length(); ++i)
                os << str[i];
        }
        return os;
    }

// End -> Editable
//--------------------------------------------------------------------------
// Begin -> No-Editable     
int main() {
        int a, b;
        double s, t;
        string mr, ms;
        cin >> a >> b >> s >> t ;
        cin >> mr >> ms ;
        cout << a << " " << b << " " ;
        cout << s << " " << t << " " ;
        cout << mr << " " << ms ;

        return 0;
    }
// End -> Non-Editable
//End Program

Inputs and outputs Input

 30 20 5.6 2.3 hello world 

Output

30
20
5.6
2.3
hello
world
Brij Raj Kishore
  • 1,595
  • 1
  • 11
  • 24

3 Answers3

6

" " is a string-literal of length one, and thus has type const char[2]. std::string is not related.

Theoretically, you could thus overload it as:

auto& operator<<(std::ostream& os, const char (&s)[2]) {
    return os << (*s == ' ' && !s[1] ? +"\n" : +s);
}

While that trumps all the other overloads, now things get really hairy. The problem is that some_ostream << " " is likely not uncommon, even in templates, and now no longer resolves to calling the standard function. Those templates now have a different definition in the affected translation-units than in non-affected ones, thus violating the one-definition-rule.

What you should do, is not try to apply a global solution to a very local problem:

Preferably, modify your code currently streaming the space-character.
Alternatively, write your own stream-buffer which translates it as you wish, into newline.

Community
  • 1
  • 1
Deduplicator
  • 44,692
  • 7
  • 66
  • 118
  • I need it locally but could you mention what could be the possible dangerous consequences of above solution and I till now I wasn't aware of user defined literals that have been mentioned in your solution. Can you post some link related to writing own stream buffer. – Brij Raj Kishore Oct 03 '18 at 06:19
  • 1
    The consequences of violating the one-definition-rule are: Program is "ill-formed, no diagnostic required", which is equivalent to the better known "Undefined Behavior", and means anything may happen. – Deduplicator Oct 03 '18 at 06:27
  • @BrijRajKishore, One definition rule violation can be avoided. There are 2 ways to get a one-definition-rule violation. 1) By ADL over dependent name at the point of instantiation of templates function that could call this operator; such a thing could happen if you declare this operator in namespace std. 2) By variations of unqualified name look-up of non-dependent name. To avoid this one, there are many options. The stricter one consist in declaring the operator in a dedicated namespace and use a using directive in the body of function that should call it. – Oliv Oct 03 '18 at 09:39
  • @BrijRajKishore On the other hand, declaring such an overload which does not have any user declared parameter type is prohibited by the standard, but you will not have any trouble if you avoid one-definition-rule violation. – Oliv Oct 03 '18 at 09:53
2

Sure this is possible, as I have tested. It should be portable since you are specifying an override of a templated function operator<<() included from <iostream>. The " " string in your code is not a std::string, but rather a C-style string (i.e. a const char *). The following definition works correctly:

ostream& operator << (ostream& os, const char *str) {
    if(strcmp(str, " ") == 0) {
        os << '\n';
    } else {
        // Call the standard library implementation
        operator<< < std::char_traits<char> > (os, str);
    }
    return os;
}

Note that the space after std::char_traits<char> is necessary only if you are pre-c++11.

Edit 1

I agree with Deduplicator that this is a potentially dangerous solution as it may cause undesirable consequences elsewhere in the code base. If it is needed only in the current file, you could make it a static function (by putting it within an unnamed namespace). Perhaps if you shared more about the specifics of your problem, we could come up with a cleaner solution for you.

mikebob
  • 311
  • 1
  • 5
1

You might want to go with a user defined literal, e.g.

struct NewLine {};

std::ostream& operator << (std::ostream& os, NewLine)
{
   return os << "\n";
}

NewLine operator ""_nl(const char*, std::size_t) // "nl" for newline
{
   return {};
}

This can be used as follows.

int main(int, char **)
{
    std::cout << 42 << ""_nl << "43" << ""_nl;

    return 0;
}

Note three things here:

  • You can pass any string literal followed by the literal identifier, ""_nl does the same thing as " "_nl or "hello, world"_nl. You can change this by adjusting the function returning the NewLine object.
  • This solution is more of an awkward and confusing hack. The only real use case I can imagine is pertaining the option to easily change the behavior at a later point in time.
  • When doing something non-standard, it's best to make that obvious and explicit - here, the user defined literal indeed shines, because << ""_nl is more likely to catch readers' attention than << " ".
lubgr
  • 37,368
  • 3
  • 66
  • 117
  • IMHO a global object of type `NewLine` is better than user defined literal. (and prevent you from doing something like `"this message gone"_nl`) – apple apple Oct 03 '18 at 06:31
  • @appleapple I do agree, but I tried to be as close as possible to the OP's original intent, hence the detour via the literal. You could also check the second argument in the string literal function and if it's non-zero, initialize some string state in `NewLine` that is printed in the `operator<<` overload then. – lubgr Oct 03 '18 at 06:33