Substitution Cipher: Which One?
Avoid using namespace std

It's a bad idea to import large namespaces such as std into the global namespace, and it could possibly even change the meaning of your code when a later standard adds new identifiers.  Reserve using namespace for those few namespaces specifically designed to be used that way (notably std::literals).

Use const appropriately

We don't want alphabet or key to be modified, so prevent accidents by declaring them as const std::string.

getline() can fail

If the input stream is closed, getline() will read nothing, and the string will be empty.  That's probably acceptable for this program, but probably worth a comment to show that you've considered this.

Use range-based for

We have an integer loop whose value is used only for indexing into the string:


for(size_t i{0}; i&lt;secretMessage.length(); ++i){
    // use secretMessage[i] here



That's easier to read with modern range-based syntax:

for (auto const c: secretMessage) {
    // use c here


Use standard algorithms

We can include &lt;algorithm&gt; to get some really useful functions from the standard library.  In particular, std::find() could replace the loop that searches in the alphabet.

Reduce duplication

Notice that there are two blocks of code that are almost identical, due to the symmetric nature of this cipher.  The only difference is that the role of alphabet and key are swapped.  This makes it a good candidate to extract as a function:

std::string substitute(const std::string&amp; message,
                       const std::string&amp; from,
                       const std::string&amp; to)


Then we could just call it:

auto encrypted = substitute(secretMessage, alphabet, key);
auto decrypted = substitute(encrypted, key, alphabet);


Avoid linear search

We can be more efficient with our lookup than simple linear search.  For example, we could construct a pair of std::map&lt;char,char&gt; from the alphabet and key.

Clean up the plaintext

I know this is just toy encryption, but for real code, we'd want to overwrite the decrypted text when we finish using it, to reduce the ability for an attacker to access it (e.g. from a core dump).  Remembering &lt;algorithm&gt;, we'd use std::fill() for this.

Omit the return

In main() (and only there), we can omit the return statement and the compiler will automatically return 0 for us.  It's a fairly common convention to omit the return if we don't have any non-zero (i.e. error) returns from main().