Overload << operator to change “ ” to “\\n”

2019-06-23 18:27发布

问题:

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

回答1:

" " 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.



回答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.



回答3:

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 << " ".