c++if-statementrecursioncin

Why does the expression !(cin>>word) cause infinite recursion?


I wrote a recursive function that reverses the order of words, but the function does not give an output when it takes a sentence as input. The following is my code:

void reverseWord(void){
    string word;
    if(!(cin>>word)){
        return;
    }
    reverseWord();
    cout<<word<<" ";
    
} 

But in the "C++ Primer(fifth edition)", there is such a sample code for reading an unknown number of strings:

int main(){
    string word;
    while(cin>>word)
        cout<<word<<endl;
    return 0;

}

What does the expression “!(cin<<word)” return? And how to modify the if-statememt?


Solution

  • Why does the expression !(cin>>word) cause infinite recursion?

    There is nothing in your code that is causing the infinite recursion! The problem is generating eof (end-of-file) on the input stream.

    Generating eof from the keyboard, (i.e., from std::cin)

    On Windows systems, you can press Ctrl+Z to generate an eof condition from the keyboard. That will cause cin.eof() to become true, and subsequent input will fail.

    It's a little tricky, though, because Ctrl+Z has to be the first character of a line for this to work. So, after you enter a sentence, you will have to do three things.

    1. Press Enter to input the sentence. That allows your program to process the sentence. Nothing will be displayed, however, until the end of step 3.
    2. Press Ctrl+Z at the start of the next line.
    3. Press Enter to input the the Ctrl+Z from step 2.

    After step 3, the sentence will be displayed with its words in reverse order.

    On Unix-like systems, Ctrl+D will sometimes do the same thing.

    Note that Ctrl+Z and Ctrl+D are not "eof characters." There is no such thing. They are regular (albeit, unprintable) characters, which, under certain system-dependent circumstances, will cause std::cin to be placed into an end-of-file state.

    Demonstration using std::stringstream

    One way to demonstrate that your program is fine, is to add a std::istream& as an argument, and use a stringstream. When the stringstream runs out of characters, it will fail.

    std::string s{ "This is a test." };
    std::stringstream sst{ s };
    if (!(sst >> word))  // `sst` hits eof when it (successfuly) inputs "test."
    {                    // The next input attempt after that will cause `sst` to fail.
        // ...
    }
    

    Here is the complete program.

    // main.cpp
    #include <iostream>
    #include <sstream>
    #include <string>
    
    void reverseWord(std::istream& ist) {
        std::string word;
        if (!(ist >> word)) {
            return;
        }
        reverseWord(ist);
        std::cout << word << " ";
    }
    
    int main()
    {
        std::string s{ "This is a test." };
        std::stringstream sst{ s };
        reverseWord(sst);
    }
    // end file: main.cpp
    

    Output

    test. a is This
    

    Use a while loop

    A commenter pointed out that you could solve this using a while-loop, instead of recursion. That requires storing the words for later retrieval. A std::stack would do the job nicely.

    void reverseWord2(std::istream& ist) {
        std::stack<std::string> words;
        std::string word;
        while (ist >> word) {
            words.push(word);
        }
        while (!words.empty()) {
            std::cout << words.top() << " ";
            words.pop();
        }
    }