1

Take the following function (which obviously doesn't compile).

const std::vector<int>& getMyVector()
{
    if (something)
        return std::vector<int>();

    if (somethingElse)
        return m_myVector;

    return std::vector<int>();
}

Assume that m_myVector is large or is otherwise not well suited to being returned by copy and so must be returned by reference.

What is the most efficient way to structure the code to allow this logic while also allowing a return by reference? I can see two options. The first would be to change this to return a pointer to the vector instead of a reference and then return null instead of an empty vector. The other would be to have a static const empty vector and return that. I don't particularly like either of those options, so I'm wondering if there's a better way.

Edit: Please don't use the example function as a literal representation of what I'm trying to illustrate. The point is that I have a function that can either return a real vector full of data or it can return a default empty vector based on certain conditions. Also, if it matters, my real code isn't even returning a simple member variable, it's a vector that's stored inside a hash_map.

TheChemist
  • 352
  • 2
  • 12
  • "_which obviously doesn't compile_": It should compile (with warnings). It is valid C++, although using the returned reference to access the object if it was produced by a `return std::vector();` will cause undefined behavior. – user17732522 Jan 25 '22 at 21:53
  • 1
    Since the `something` case has the same result as the default case, you should be able to eliminate the `something` case. – user4581301 Jan 25 '22 at 21:53
  • 1
    A `const` reference is returned, so it looks like a safe place for a `static` empty `vector` for you to return. – user4581301 Jan 25 '22 at 21:56
  • Returning a reference to a local object that will be destroyed at function exit? But why? Also returning a single vector (by value) can help the compiler take advantage of RVO so that you'll have no moves or copies at all. But if you have braches like that, then RVO most likely will not be possible. – digito_evo Jan 25 '22 at 21:56
  • 1
    You can always use `std::optional`, it's not the most efficient way but it's quite elegant. https://en.cppreference.com/w/cpp/utility/optional – Aleix Rius Jan 25 '22 at 21:58
  • 3
    A pointer seems like it would work well. `boost::optional&>` would serve the same purpose and might be more clear or safe. You also might want to consider returning a span/view that is possibly empty – Artyer Jan 25 '22 at 21:59
  • 1
    Some clarity may be needed here - _"I don't particularly like [working solution], so I'm wondering if there's a better way."_ Why don't you like it? What would make a different way better (or worse)? – Drew Dormann Jan 25 '22 at 22:00
  • @Artyer really like the `span` idea. – user4581301 Jan 25 '22 at 22:01
  • 1
    Returning a vector should be fine because of C++ return value optimization (RVO). If you're returning different vectors based on conditions, then are you allowed to modify the function arguments? If so, the function can take in a vector by reference which can be used to store the results. – heothesennoc Jan 25 '22 at 22:06
  • @DrewDormann The const static I don't like because it's a random variable in the code that's used for nothing except a return value. The pointer version creates a possibility of error and means you have to handle a null differently than just an empty vector. The pointer is definitely the solution I was leaning towards before posting the question, though. – TheChemist Jan 25 '22 at 22:48
  • @heothesennoc Returning by-value will cause a full vector copy in case `m_myVector` is returned. – user17732522 Jan 26 '22 at 09:17
  • @TheChemist based on your concerns, I would recommend using a const static variable. Your concern that "it's random" doesn't carry any meaning that I know of. And your concern that it's "used for nothing except [its intended purpose]" is intentional. – Drew Dormann Jan 26 '22 at 13:33
  • @user17732522 in the given example, yes it will as it's returning different vectors based on conditions. – heothesennoc Jan 26 '22 at 15:00
  • @heothesennoc The point of the question is to avoid that. – user17732522 Jan 26 '22 at 15:04

1 Answers1

3

Despite your hesitations, I recommend defining a static empty result vector wherever you have defined m_myVector.

std::vector<int>              m_myVector;
static const std::vector<int> m_emptyResult;

Your code can then both compile and avoid copies.

const std::vector<int>& getMyVector()
{
    if (something)
        return m_emptyResult;

    if (somethingElse)
        return m_myVector;

    return m_emptyResult;
}

Or more tersely:

const std::vector<int>& getMyVector()
{
    if ( !something && somethingElse )
        return m_myVector;

    return m_emptyResult;
}
Drew Dormann
  • 59,987
  • 13
  • 123
  • 180