Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can a function in C++ return either a value or a reference with minimal copying?

Tags:

c++

c++11

I have a function that delegates to two others, returning either a reference or value depending on some runtime condition:

X by_value() { ... }
const X& by_reference() { ... }

?? foo(bool b) {
  if (b) {
    return by_value();
  } else {
    return by_reference();
  }
}

I'd like to choose the return type of my function so that callers induce minimal copying; e.g.:

const X& x1 = foo(true);   // No copies
const X& x2 = foo(false);  // No copies
X x3 = foo(true);          // No copies, one move (or zero via RVO)
X x4 = foo(false);         // One copy

In all cases except the last, there shouldn't be a need (based on the runtime behavior) to copy the return value.

If the return type of foo is X, then there will be an extra copy in case 2; but if the return type is const X&, then cases 1 and 3 are undefined behavior.

Is it possible, via returning some sort of proxy, to ensure that the above uses have minimal copies?


Explanation: Since there's been significant pushback of the form "you're doing it wrong", I thought I'd explain the reason for this.

Imagine I have an array of type T or function<T()> (meaning the elements of this array are either of type T, or they're functions returning T). By the "value" of an element of this array, I mean, either the value itself or the return value when the function is evaluated.

If this get_value_of_array(int index) returns by value, then in the cases where the array contains just an element, I'm forced to do an extra copy. This is what I'm trying to avoid.


Further note: If the answer is, "That's impossible", that's fine with me. I'd love to see a proof of this, though - ideally of the form "Suppose there were a type Proxy<X> that solved your problem. Then...`

like image 727
Jesse Beder Avatar asked Jun 21 '14 18:06

Jesse Beder


People also ask

How a function can return a reference?

A C++ function can return a reference in a similar way as it returns a pointer. When returning a reference, be careful that the object being referred to does not go out of scope. So it is not legal to return a reference to local var. But you can always return a reference on a static variable.

Is a function required to return a value in C?

Return Type Some languages differentiate between "subroutines", which do not return a value, and "functions", which do. In C there are no subroutines, only functions, but functions are not required to return a value. The correct way to indicate that a function does not return a value is to use the return type "void".

Should a function contain a return statement if it does not return a value in C?

If a return value isn't required, declare the function to have void return type. If a return type isn't specified, the C compiler assumes a default return type of int . Many programmers use parentheses to enclose the expression argument of the return statement. However, C doesn't require the parentheses.

Is C pass by value or reference?

C always uses 'pass by value' to pass arguments to functions (another term is 'call by value', which means the same thing), which means the code within a function cannot alter the arguments used to call the function, even if the values are changed inside the function.


2 Answers

What you are looking for is a sum-type (that is, a type whose possible values are "the possible X values plus the possible X const& values").

In C++, these are usually called variant. These are usually implemented as a tag plus an appropriately sized and aligned array, and only hold exactly one value at runtime. Alternatively, they are implemented with dynamic allocation and the classic visitor pattern.

For example, with Boost.Variant, you could declare your function to return boost::variant<X, X const&> (live example):

boost::variant<X, X const&> foo(bool b) {
  if (b) {
    return by_value();
  } else {
    return by_reference();
  }
}
like image 60
Mankarse Avatar answered Sep 18 '22 12:09

Mankarse


I think this is impossible because whether the caller decides to move or copy the return value (whether it's from a proxy or from your class itself) is a compile-time decision, whereas what you want is to make it a run-time decision. Overload resolution cannot happen at run-time.

The only way out that I can see is to have the callee decide this, i.e. by providing a T & parameter which it can either move-assign to or copy-assign to depending on what it deems appropriate.
Alternatively, you can pass an aligned_storage<sizeof(T)> buffer and have the callee construct the value inside it, if you don't think the caller can be expected to make a "null" instance of some sort.

like image 38
user541686 Avatar answered Sep 21 '22 12:09

user541686