C++ Caesar cipher - follow-up I asked another question the other day and decided to rewrite the code with some of the suggestions given and add functionality (instead of reading from a console I'm reading directly from a file) the code works.

Looking at it, I think the nested for loop on the encode function doesn't look that good. I tried using std::transform like the comment on the code. It worked when I used the console just to read a line, now with vectors couldn't make it work.

// Ceaser Cipher implementation

#include &lt;iostream&gt;
#include &lt;string&gt;
#include &lt;algorithm&gt;
#include &lt;fstream&gt;
#include &lt;vector&gt;


std::vector&lt;std::string&gt; encode(const std::vector&lt;std::string&gt; &amp;str, int shift)
{
    std::vector&lt;std::string&gt; tempMsg;

    for (std::string lines : str)
    {
        // std::transform(lines.cbegin(), lines.cend(), std::back_inserter(tempMsg), [&amp;](char ch) -&gt; char
        for (char &amp;ch : lines)
        {
            if (ch == 'z' || ch == 'Z')
            {
                ch -= 25;
            }
            else if (isspace(ch)) {
                ch = ' ';
            }
            else
            {
                ch += shift;
            }
        }
        tempMsg.push_back(lines);
    }

    return tempMsg;
}

std::vector&lt;std::string&gt; decode(const std::vector&lt;std::string&gt; &amp;str, int shift)
{
    return encode(str, -1 * shift);
}


int main(int argc, char *argv[])
{
    int choice;
    std::cout &lt;&lt; "What do you want to do? 1.Encrypt, 2.Decrypt: ";
    std::cin &gt;&gt; choice;


    int key;
    std::cout &lt;&lt; "Enter desired shift: ";
    std::cin &gt;&gt; key;


    std::ifstream inFile("testfile.txt");
    if (!(inFile.is_open()))
    {
        std::cout &lt;&lt; "There was a problem with the file!";
        std::exit(EXIT_FAILURE); // ExitProcess() if windows especific.
    }

    std::vector&lt;std::string&gt; finalResult;

    std::string line;
    std::vector&lt;std::string&gt; lines;


    while (std::getline(inFile, line))
    {
        lines.push_back(line);
    }
    inFile.close();

    if (choice == 1)
    {
        auto result = encode(lines, key);
        finalResult = result;
    }

    else if (choice == 2)
    {
        auto result = decode(lines, key);
        finalResult = result;
    }
    else
    {
        std::cout &lt;&lt; "Wrong option, exiting!";
        std::exit(EXIT_FAILURE);
    }


    std::ofstream outFile("testfile.txt");
    for (auto i = finalResult.begin(); i != finalResult.end(); ++i)
    {
        outFile &lt;&lt; *i &lt;&lt; '\n';
    }
    outFile.close();

    std::exit(EXIT_SUCCESS);
}
In the following, I am omitting checking for the correctness of the outputs as you have not provided a specification for what that should be. Nevertheless, consider the following points.


I think the signatures of your encoding and decoding functions are a bit unexpected: I would expect an encoding function to take a single string and not a vector of strings. This gives me the feeling that the function is doing too much (i.e., violating the guideline of one function, one responsibility). So I would break the logic into smaller pieces:

std::string encode(const std::string&amp; str, int shift)
{
    std::string str_enc;
    std::transform(str.cbegin(), str.cend(), std::back_inserter(str_enc), [&amp;](char ch) -&gt; char 
    { 
        if (ch == 'z' || ch == 'Z')
        {
            return ch - 25;
        }
        else if (isspace(ch)) 
        {
            return ' ';
        }
        else
        {
            return ch + shift;
        }
    });

    return str_enc;
}

std::vector&lt;std::string&gt; encode(const std::vector&lt;std::string&gt;&amp; str, int shift)
{
    std::vector&lt;std::string&gt; tempMsg;
    std::transform(str.cbegin(), str.cend(), std::back_inserter(tempMsg),
        [&amp;](const std::string&amp; s) { return encode(s, shift); });

    return tempMsg;
}

In the main loop logic, before getting into any file reading, check whether the choice made by the user is sensible (either 1 or 2). This allows you to make finalResult const and to initialize it in a nicer way. This is a good modern practice as given by e.g., ES.28 of the C++ Core Guidelines:

assert(choice == 1 || choice == 2);
const std::vector&lt;std::string&gt; finalResult = [&amp;]()
{
    return choice == 1 ? encode(lines, key) : decode(lines, key);
}(); 

You don't have to explicitly close file streams. They close upon destruction.
To check if the file is good to go, you can do if(inFile) { ... } invoking its operator bool() which tells you if the stream has no errors and is ready for I/O operations (specifically, it returns !fail()).
When writing to file, you can use standard algorithms (like std::copy) since your logic is very simple:

std::ofstream outFile("outfile.txt");
std::copy(finalResult.cbegin(), finalResult.cend(), 
    std::ostream_iterator&lt;std::string&gt;(outFile, "\n"));

Everywhere in your code, avoid using std::exit (for reasons and more discussion, see here). Instead, just do return EXIT_FAILURE or return EXIT_SUCCESS as appropriate.