Prompt user to choose from menu
Answers to your questions

While it considers Ctrl-D as an invalid choice, should it be handled differently? If so, how?

You should treat the end of the file as the definite end of the input, and return immediately with a value that indicates a failure. Not all EOFs are Ctrl-D's, there is no guarantee that you can even continue reading from std::cin after you encounter an EOF; for example the user might have redirected standard input to a file.

It takes user input as an int. Would you suggest taking it as a string instead, and then use either stoi or stringstream to convert it to an int?

As you have probably seen std::cin &gt;&gt; some_int does not read a whole line, instead it tries to read an integer but stops as soon as it encounters something that is not an integer. Instead of fighting against this, use std::getline() if you want to read a line, and then indeed use std::stoi() or something similar to convert that to a number. You still need to deal with the fact that also stoi() stops as soon as it sees the first character that is not part of an integer.

Can it be re-written in a more elegant and concise way?

Yes, see below.

Do you see any potential issues?

There is code duplication and unnecessary complexity dealing with std::cin. Also, the function is not very generic; if you want to get a choice for a different menu you would have to write a new function.
Reading a line with an integer in it
First, read a single line, and check if that succeeded. If not, either return some value indicating an error, or throw an exception. For example:
std::string line;

if (!std::getline(std::cin, line)) {
    throw std::runtime_error(&quot;Error while reading input&quot;);
}

Then try to convert it with std::stoi() or any of its variants, depending on the type of integer. Use the second argument to std::stoi() to know where it stopped parsing the integer, and check that this was at the end of the string:
try {
    size_t endpos;
    size_t value = stoi(line, &amp;endpos);

    if (endpos == line.size()) {
        // Success!
        ...
    }
} catch (std::logic_error &amp;) {
    // Error parsing the integer
    ...
}

Make getChoice() more generic
You hardcoded the choices in getChoice(). If you have a program with multiple menus, then you would have to write different versions of getChoice(), duplicating a lot of code. So try to make it more generic. For example, you can have getChoice() take two parameters: one is a string with the title of the menu, the second is a vector of strings with the possible choices.
Full example
Here is an example of what it could look like:
#include &lt;iostream&gt;
#include &lt;stdexcept&gt;
#include &lt;string&gt;
#include &lt;vector&gt;

size_t getChoice(const std::string &amp;title, const std::vector&lt;std::string&gt; &amp;choices)
{
    std::cout &lt;&lt; title &lt;&lt; &quot;\n&quot;
              &lt;&lt; &quot;-------------------\n&quot;;

    for (size_t i = 0; i &lt; choices.size(); ++i) {
        std::cout &lt;&lt; i &lt;&lt; &quot;. &quot; &lt;&lt; choices[i] &lt;&lt; &quot;\n&quot;;
    }

    while (true) {
        std::cout &lt;&lt; &quot;Enter choice: &quot; &lt;&lt; std::flush;
        std::string line;

        if (!std::getline(std::cin, line)) {
            throw std::runtime_error(&quot;Could not read input&quot;);
        }

        try {
            size_t endpos;
            size_t choice = stoul(line, &amp;endpos);

            if (endpos == line.size() &amp;&amp; choice &lt; choices.size()) {
                return choice;
            }
        } catch (std::exception &amp;) {
            // nothing to do here
        }

        std::cout &lt;&lt; &quot;Invalid choice\n&quot;;
    }
}

int main()
{
    static const std::vector&lt;std::string&gt; choices = {
        &quot;Add record&quot;,
        &quot;Edit record&quot;,
        &quot;Delete record&quot;,
        &quot;View record&quot;,
        &quot;view all records&quot;,
        &quot;Exit&quot;,
    };

    size_t choice = getChoice(&quot;Main menu&quot;, choices);

    std::cout &lt;&lt; &quot;User chose option &quot; &lt;&lt; choice &lt;&lt; std::endl;
}

Note that I made the option numbers start with zero, just to keep the code slightly simpler. I also used size_t, as it is unsigned (no need to deal with negative numbers), and it's good practice whenever you deal with indices into arrays, vectors and so on. Note that you don't have to create a named vector in main(), you can even pass a literal to getChoices():
size_t choice = getChoice(&quot;Menu&quot;, {&quot;First option&quot;, &quot;Second option&quot;, ...});