79

I am wondering about this because of scope issues. For example, consider the code

typedef struct {
    int x1;/*top*/
    int x2;/*bottom*/
    int id;
} subline_t;



subline_t subline(int x1, int x2, int id) {
    subline_t t = { x1, x2, id };
    return t;
}

int main(){
    subline_t line = subline(0,0,0); //is line garbage or isn't it? the reference
    //to subline_t t goes out of scope, so the only way this wouldn't be garbage
    //is if return copies
}

So my question is, will the return statement always copy? In this case it seems to work, so I am led to believe that return does copy. If it does copy, will it copy in every case?

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
ldog
  • 11,707
  • 10
  • 54
  • 70
  • There are no references (as in `&` - a C++ reference) to `subline_t` in your code. – Pavel Minaev Oct 07 '09 at 04:28
  • @gmatt: Are you thinking like Java/C#, where every object is a reference to something? This is not the case in C/C++ world. – Donotalo Oct 07 '09 at 06:26
  • @Donotalo: to tell you the truth, I just plain didn't know the behaviour of C/C++ when returning a variable in local scope. Well, I knew that its fine to do with integers for example, but I also knew that you had to be careful in some cases (like returning a pointer to an object instantiated locally.) I didn't know the behaviour that C/C++ adopts for structs. – ldog Oct 07 '09 at 16:44
  • @MSalters: next time you edit someones code make sure it atleast compiles ..... – ldog Jun 25 '10 at 20:12

11 Answers11

54

Yes, in that case there will be a copy made. If you change the function declaration like this:

subline_t &subline(int x1, int x2, int id) {

then no copy will be made. However, in your specific case it would not be valid to return a reference to an object allocated on the stack. The problem is that the object would be destructed and invalidated before the caller had a chance to use it.

This is related to the common Return Value Optimization for C++ that can avoid doing an actual copy operation in the case you have described. The end result is (or should be) the same as if a copy were done, but you should be aware of the optimization. The presence of this optimization can, in some cases, change the observable behaviour of the program.

Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
  • The link you present says that the standard allows the optimization, as long as it doesnt change the behaviour of the program: "In general, the C++ standard allows a compiler to perform any optimization, as long as the resulting executable exhibits the same observable behaviour as if all the requirements of the standard has been fulfilled" – Tom Oct 07 '09 at 04:24
  • 9
    If you've read further, you would have also seen that the linked article says afterwards: "The term return value optimization refers to a special clause in the C++ standard that allows an implementation to omit a copy operation resulting from a return statement, even if the copy constructor has side effects, something that is not permitted by the as-if rule in itself." – Pavel Minaev Oct 07 '09 at 04:26
  • 1
    @Greg - At first sight, you're suggesting returning a reference to a local variable - ie a dangling pointer. With POD, this particular program will work in practice, but... –  Oct 07 '09 at 04:32
  • 3
    @Steve314: With the `&`, no copy will be made in the case described in the question, but the resulting program operation would be undefined. However, it would be perfectly legal if the object being returned were not a locally scoped variable. – Greg Hewgill Oct 07 '09 at 04:39
  • 20
    You shouldn't return reference to temporary object. – Kirill V. Lyadvinsky Oct 07 '09 at 05:23
  • Not sure how I missed mentioning the problem with returning a reference to a temporary when I first wrote this. Fixed. – Greg Hewgill May 02 '14 at 21:34
5

In your case , it will return a copy

If your code was

subline_t& subline(int, int)

then it would return a reference, which would yield in undefined behaviour.

Tom
  • 43,810
  • 29
  • 138
  • 169
  • 4
    Returning a reference is not undefined behavior in and of itself. Returning a reference to a local is, but returning a reference to global, static, field of `this`, or heap-allocated object is perfectly fine. – Pavel Minaev Oct 07 '09 at 04:27
  • 1
    Careful with heap-allocated objects though - the caller probably won't expect to be responsible for deleting that object if you return a reference. –  Oct 07 '09 at 04:58
  • 1
    @Steve314: Pavel didn't say the caller was responsible for freeing it, he said it was heap-allocated. Maybe someone else is responsible for freeing it, but the "subline" call returns a view of it. This could happen if for example "subline" was returning cached values. Obviously the caller would need to know the dynamic scope of the object (that is, how long it can be relied on to remain valid). – Steve Jessop Oct 07 '09 at 15:32
  • 1
    Returning a reference to a local object does not cause UB if the caller does not use the return value – M.M Oct 05 '14 at 23:35
  • @M.M does use mean also mean storing the returned reference in some variable or doing something more with it(which i think is surely UB)? – asn Apr 17 '20 at 00:26
3

Yes, for a function declared to return a struct, return of such a struct will copy it (though the compiler is empowered to optimize the copy away, essentially in cases where it can prove the optimization is semantically innocuous, you can reason "as if" the copying was guaranteed).

However, since you did tag this as C++, not C, why not supply your struct with a constructor, instead...? Seems clearer and more direct...!-)

Alex Martelli
  • 854,459
  • 170
  • 1,222
  • 1,395
  • 1
    Why supply a `struct` with a copy constructor when it doesn't do anything different from what a default copy constructor does? Also, doing so would make it non-POD, which may be important elsewhere. – Pavel Minaev Oct 07 '09 at 04:28
  • if instead of a struct I used a class in C++, would it still copy? – ldog Oct 07 '09 at 04:31
  • I mean, if the code was identical with only syntatic changes of struct to class. – ldog Oct 07 '09 at 04:32
  • 2
    It still would. The only difference between `class` and `struct` is that `class` members and base classes are `private` by default, while `struct` members and base classes are `public` by default. Otherwise they work exactly the same. – Pavel Minaev Oct 07 '09 at 04:45
  • @Pavel, I did not say _copy_ constructor -- that `subline` function should just morph into a `subline_t` ctor, it's just handier! – Alex Martelli Oct 07 '09 at 04:57
  • 1
    I suspect the 'typedef struct' is a clue that very little C++ is happening here, if any. –  Oct 07 '09 at 05:06
  • I "fixed" the `typedef struct`, but yes, the original was in that weird C/C++ language. – MSalters Oct 07 '09 at 11:36
2

yes , the return is a copy

subline_t subline(int x1, int x2, int id) {
        subline_t t = { x1, x2, id };
        return t;
}

If you put a referencer, then its not a copy

subline_t & subline(int x1, int x2, int id) {
        subline_t t = { x1, x2, id };
        return t; // will result in corruption because returning a reference
}
Andrew Keith
  • 7,515
  • 1
  • 25
  • 41
2

It will always return a copy.

If you want to avoid the performance hit of copying the object on return, you can declare a pointer, build an instance of the object using new, and return the pointer. In that case, the pointer will be copied, but the object won't be.

csj
  • 21,818
  • 2
  • 20
  • 26
  • 3
    It might still be often wiser to rely on return value optimization. I think GCC does that even with -O0 and you'll need to use the -fno-elide-constructors switch if you want the copying to happen. – UncleBens Oct 07 '09 at 08:32
1

Returing objects in C++ done by value and not by reference.

the reference to subline_t t goes out of scope

No, the object is copyed.

will the return statement always copy

Yes and not... Semantically it behaves like copy, but there is something that is called return value optimization that saves copy constructor.

foo make_foo()
{
    foo f(1,2,3);
    return f;
}

foo ff=make_foo(); /// ff created as if it was created with ff(1,2,3) -- RVO
foo ff2;
ff2=make_foo(); /// instance of foo created and then copied to ff2 and then old
                /// instance destroyed
Artyom
  • 31,019
  • 21
  • 127
  • 215
  • Good, but there's no guarantee that RVO will kick in. The compiler still has freedom to copy, and it _must_ check for a presence of a valid copy constructor even if it chooses to elide the copy. – Pavel Minaev Oct 07 '09 at 04:30
1

It returns a copy, which is what you want it to do. Changing it to return a reference will result in undefined behaviour in the assignment to line.

However, the idiomatic way to do this in C++ is with constructors and assignment lists. This encapsulates code and data structures better, and allows you to avoid the plethora of intermediate objects that compilers are free to construct/destruct/copy.

struct subline_t {
        int x1;/*top*/
        int x2;/*bottom*/
        int id;

// constructor which initialises values with assignment list.
  subline_t(int the_x1, int the_x2, int the_id) :
    x1(the_x1),
    x2(the_x2),
    id(the_id)
  {
  }
};


int main(){
    subline_t line2(0,0,0); // never requires a copy or assignment.
}
Roddy
  • 66,617
  • 42
  • 165
  • 277
1

The returned class or struct may or may not be copied, depending if the compiler uses copy elision. See the answers to What are copy elision and return value optimization? In short, whether it is copied or not depends on a number of things.

You can of course avoid a copy by returning a reference. In the case of your example, returning a reference is invalid (though the compiler will allow it) because the local struct is allocated on the stack, and therefor the returned reference refers to a deallocated object. However, if the object was passed to your function (directly or as a member of an object) you can safely return a reference to it and avoid copy-on-return.

Finally, if you cannot trust copy elision and you want to avoid copies, you can use and return a unique_ptr instead of a reference. The object itself will not be copied, although the unique_ptr itself may or may not be (again, depending on copy elision!). Copying/moving a unique_ptr is however very cheap if copy elision of the unique_ptr does not happen for some reason.

Here is an example using unique_ptr:

#include <memory>

struct A {
public:
  int x;
  int y;

  A(int x, int y) : x(x), y(y) {
  }
};

std::unique_ptr<A> returnsA() {
  return std::make_unique<A>(3, 4);
}

int main() {
  auto a = returnsA();
}

Note that you must (unfortunately) declare a constructor for your struct, or else make_unique will not compile due to inadequacies of C++.

Alex K.
  • 133
  • 2
  • 5
0

For the structure subline_t you've defined, yes, it will always return a copy.

Jacob
  • 34,255
  • 14
  • 110
  • 165
-1

Just FYI, since in this case you are using only a struct(ure), it is the same behavior as in C language.

Though this is a language feature, it is recommended that it shall not be used

Alphaneo
  • 12,079
  • 22
  • 71
  • 89
  • What is a language feature in question, and why is it recommended that it shouldn't be used? – Pavel Minaev Oct 07 '09 at 04:30
  • @Pavel, this is a nice question. It is because, we would be copying the data twice. Once during setting the structure inside the routine, and once during copying the return data. – Alphaneo Oct 07 '09 at 04:46
  • Also on premature optimisation. Code to share resources in order to "avoid a copy" may well be slower than just copying a tiny struct like this – Steve Jessop Oct 07 '09 at 15:34
-2

I do not agree and NOT RECOMMEND to return vector to change values: Is much faster pass as reference:

void vectorial(vector <double> a, vector <double> b, vector <double> &c)
{
    c[0] = a[1] * b[2] - b[1] * a[2]; c[1] = -a[0] * b[2] + b[0] * a[2]; c[2] = a[0] * b[1] - b[0] * a[1];
}
//This is slow!!!:
vector <double> vectorial(vector <double> a, vector <double> b)
{
    vector <double> c{ a[1] * b[2] - b[1] * a[2], -a[0] * b[2] + b[0] * a[2], a[0] * b[1] - b[0] * a[1] };
    return c;
}

I tested on VS2015 with following results in release mode:

By reference:8.01 MOPs and returning vector: 5.09 MOPs 60% worse!

In debug mode things are much worse:

By reference:0.053 MOPS and return vector: 0.034 MOPs

mathengineer
  • 140
  • 6
  • That's because you didn't write `return std::move(c)`, and you accept vectors by value when you don't need to. – Lightness Races in Orbit Nov 15 '18 at 10:58
  • By the way your conclusions are false. The debug performance you've found is 64% of the speed of the reference version; in release it's 63%. Neither of those is "60% worse" (you _could_ describe it as "40% worse" - I still wonder why NRVO didn't kick in for you - what are your build flags?) – Lightness Races in Orbit Nov 15 '18 at 11:00
  • Here is the corrected code for you to retest: http://coliru.stacked-crooked.com/a/7e2448d9cbcfafa3 – Lightness Races in Orbit Nov 15 '18 at 11:03
  • I wrote std::move(c); and obtained: 5.95MOPs and 2.31MOPs. 8/5=1.6, so a +60% is obtained (it changes something from a run to other) – mathengineer Nov 15 '18 at 11:04
  • Actually [the `std::move` may be not be necessary](https://stackoverflow.com/a/4986689/560648) but the fact remains that you're copying loads of things you don't have to and it's ruining the validity of your benchmark – Lightness Races in Orbit Nov 15 '18 at 11:05
  • "In" parameters are an outdated idiom – Lightness Races in Orbit Nov 15 '18 at 11:06
  • std: make it 9.81 vs 2.67MOPs, so by reference gains. But if you prefer return the vector and take 60%-367 more time, do it! :) . VS2015 (sorry), win7 and E6-1620 machine. NOTE: if an advise to increase the speed A LOT of processed, is marked down I will NOT WROTE HERE any more – mathengineer Nov 15 '18 at 11:12
  • Something is very wrong with your results. There is no reason for adding `std::move` (which is redundant at compile time) to make it half as fast, and certainly not for it to make your unrelated implementation faster. You need to review your benchmarking technique. The advice given across this page is given by experts. – Lightness Races in Orbit Nov 15 '18 at 11:14
  • Try it, the code is there, but take in account that if the code runs sometimes in a way and sometimes in another is not good code. My code runs in multithread using elliptic integrals within a particle physics simulator. I made also code for GPUs running vision algoritms running 4 HD video streams at the realtime. In none of them I make a copy of someting to parse arrays or structures to a function because simply IS STUPID. C and C++ code is done to RUN but not a little more, to RUN FASTER not to lose time making nice code, if not there are very wonderful languages like PYTHON – mathengineer Nov 16 '18 at 07:41
  • 1
    You should review what happens when you pass objects by value. – Lightness Races in Orbit Nov 16 '18 at 10:46