Step 1: Read one word — >> stops at the first space

#include <iostream>
#include <string>

int main() {
    std::string word;
    std::cin >> word;
    std::cout << word << '\n';
}

One word in, one word out. The string sizes itself to whatever arrives, so nothing here fixes a length in advance. >> stops at the first whitespace and leaves the rest of the line unread.

Step 2: Swap >> for getline — keep the spaces

#include <iostream>
#include <string>

int main() {
    std::string line;
    std::getline(std::cin, line);
    std::cout << line << '\n';
}

Replaced: >> with getline. The whole line arrives, spaces included. A preceding >> leaves its newline pending, and the next getline would read an empty line until that newline is consumed.

Step 3: Ask the string for its size — visit every position

#include <cctype>
#include <iostream>
#include <string>

int main() {
    std::string line;
    std::getline(std::cin, line);
    int digits = 0;
    for (std::size_t i = 0; i < line.size(); ++i) {
        if (std::isdigit(static_cast<unsigned char>(line[i]))) ++digits;
    }
    std::cout << digits << '\n';
}

Added: size() as the loop bound and a classification test. The string knows its own length, so there is no terminator to hunt for. isdigit is handed an unsigned char because a negative char value is undefined behavior there.

Step 4: Swap [] for at() — make an invalid position fail loudly

#include <cctype>
#include <iostream>
#include <string>

int main() {
    std::string line;
    std::getline(std::cin, line);
    if (line.empty()) {
        std::cout << "no text\n";
        return 0;
    }
    int digits = 0;
    for (std::size_t i = 0; i < line.size(); ++i) {
        if (std::isdigit(static_cast<unsigned char>(line.at(i)))) ++digits;
    }
    std::cout << digits << '\n';
}

Added: an empty check and at(i). An empty string has no valid position; at throws std::out_of_range instead of causing undefined behavior.

Step 5: Split on a delimiter — check find before taking a substring

#include <iostream>
#include <string>

int main() {
    std::string line;
    std::getline(std::cin, line);
    std::size_t dash = line.find('-');
    if (dash == std::string::npos) {
        std::cout << "no delimiter\n";
        return 0;
    }
    std::cout << line.substr(0, dash) << '\n';
    std::cout << line.substr(dash + 1) << '\n';
}

Added: a search and two substrings. find reports failure as std::string::npos, not -1. substr takes a position and a length, so substr(dash + 1) runs to the end.