Returning by value is inevitable?

Started by
22 comments, last by Shannon Barber 7 years, 11 months ago
I want to calculate the cross product of two vectors in a class which will be stored in temp variable. When I call on this method I cannot return it by const ref because the stack will be destroyed when the method returns I dont want to return by value because there is a lot copying if I use this method frequently.How can I make a compromise between efficiency and correctness ?
thanks
Advertisement

One trick (assuming C++) is to create a static variable inside the function, and return that. Static variables will never go out of scope.


result_type& SomeFunction(...)
{
    static result_type result;
    // ...fill result
    return result;
}

First return value also gets destroyed with the second call.

Better use a reference in the caller context instead.


void Compute(Foo &x) {
    x.a = ...; // It looks like a normal variable access, but you're writing in the variable that you provide with the call.
}

// Use
Foo f;
Compute(f);
// After the call, the result is in f

I want to calculate the cross product of two vectors in a class which will be stored in temp variable. When I call on this method I cannot return it by const ref because the stack will be destroyed when the method returns I dont want to return by value because there is a lot copying if I use this method frequently.How can I make a compromise between efficiency and correctness ?
thanks

You don't even need to. Any halfway modern compiler will perform RVO (https://en.wikipedia.org/wiki/Return_value_optimization), which means that


Vector cross(const Vector& vector, const Vector& vector2)
{
    Vector crossProduct;

    return crossPoduct;
}

const auto outCrossProduct = cross(v1, v2);

is equivalent to:


// this is somewhat along the lines of what the compiler will do with the entire code above

void cross(Vector& crossProduct, const Vector& vector, const Vector& vector2)
{
}

Vector outCrossProduct;
cross(outCrossProduct, v1, v2);

The compiler will construct the otherwise internal "crossProduct" variable directly in the "outCrossProduct"-variable, with no additional copy-constructors/destructors called. If you are really unsure about it, you can eigther check assembly or test this behaviour with putting cout-statements in your copy-ctor/dtor (those won't even be called because RVO might alter side-effects). Just make sure to turn on optimizations.

EDIT: Oh, forgot to mention, the first version of the function with RVO can probably perfom even better than if you just wrote the second one, due to aliasing. See how you have to pass in 3 references to "Vector"? The compiler has to assume all of the can point to the same memory location and thus has to produce safer, but slower code. You cannot use "restrict" here though, because taking the cross product of the same vector with itself might be a valid operation. So the second version at least can make optimizations regarding that the local "crossProduct" cannot alias with "vector" and "vector2", while in the second example it can, most likely resulting in better code generated for the first version than the second.

Anyone care to explain what is wrong with the static variable solution? I've used this for years and always thought it was a convenient and fast solution.

Anyone care to explain what is wrong with the static variable solution? I've used this for years and always thought it was a convenient and fast solution.

It's not faster nor more convenient than RVO. Also, it breaks on multi-threading.

Also, any function returning a reference should almost always guarantee that reference is safe to hold without requiring the reference return be immediately copied (otherwise, just return by value).

With your example, one cannot write: result_type & a = SomeFunction(); and expect it to work reliably. Any subsequent call to SomeFunction, including one on another thread, will overwrite the static variable named by a.

it breaks on multi-threading.


A million times this. If you're writing a library of any kind it's simply expected in 2016 that a library is generally thread safe. Functions and classes that are not need to be clearly documented as such...

I was aware of thread-safety issues, but I see how a reference that can change value - no matter how - is unacceptable.
Thanks for the explanation.

Anyone care to explain what is wrong with the static variable solution? I've used this for years and always thought it was a convenient and fast solution.

Compute (Function (x), Function (y));

If "Function" uses the static variable trick, does this work?

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls.

This topic is closed to new replies.

Advertisement