4

If structs are fully copied, then the first loop is more expensive than the second one, because it is performing an additional copy for each element of v.

vector<MyStruct> v;

for (int i = 0; i < v.size(); ++i) {
    MyStruct s = v[i];
    doSomething(s);
}

for (int i = 0; i < v.size(); ++i) {
    doSomething(v[i]);
}

Suppose I want to write efficient code (as in loop 2) but at the same time I want to name the MyStruct elements that I draw from v (as in loop 1). Can I do that?

user2460978
  • 705
  • 5
  • 17

5 Answers5

7

Structs (and all variables for that matter) are indeed fully copied when you use =. Overloading the = operator and the copy constructor can give you more control over what happens, but there is no way you can use these to change the behavior from copying to referencing. You can work around this by creating a reference like this:

for (int i = 0; i < v.size(); ++i) {
    MyStruct& s = v[i]; //& creates reference; no copying performed
    doSomething(s);
}

Note that the struct will still be fully copied when you pass it to the function, unless the argument is declared as a reference. This is a common pattern when taking structs as arguments. For instance,

void doSomething(structType x);

Will generally perform poorer than

void doSomething(const structType& x);

If sizeof structType is greater than sizeof structType*. The const is used to prevent the function from modifying the argument, imitating pass-by-value behavior.

ApproachingDarknessFish
  • 13,013
  • 6
  • 35
  • 73
3

In your first example, the object will be copied over and you will have to deal with the cost of the overhead of the copy.

If you don't want the cost of the over head, but still want to have a local object then you could use a reference.

for (int i = 0; i < v.size(); ++i) {
    MyStruct& s = v[i];
    doSomething(s);
}
Caesar
  • 8,395
  • 6
  • 34
  • 62
2

Copied*. Unless you overload the assignment operator. Also, Structs and Classes in C++ are the same in this respect, their copy behaviour does not differ as it does in c#.

If you want to dive deep into C++ you can also look up the move operator, but it is generally best to ignore that for beginners.

C++ does not have garbage collection, and gives more control over memory management. If you want behaviour similar to c# references, you can use pointers. If you use pointers, you should use them with smart pointers (What is a smart pointer and when should I use one?).

* Keep in mind, if the struct stores a pointer, the pointer in a copied struct will point to the same location. If the object in that location is changed, both structs' pointers will see the changed object.

P.S: I assume you come from a c# background based on the vocabulary in your question.

Community
  • 1
  • 1
Peter
  • 5,193
  • 1
  • 21
  • 41
  • The assignment operator is not used. – chris Feb 10 '14 at 02:40
  • Same is true of the move assignment operator. Only constructors are called here. T foo = bar; is syntactic sugar for T foo(bar); – Shirik Feb 10 '14 at 02:50
  • About the P.S: It's not the case actually. But it was an interesting read nonetheless. – user2460978 Feb 10 '14 at 02:50
  • I'm curious, which language do you come from, then? – Peter Feb 10 '14 at 02:53
  • @Peter C++ is the language I have used more and know best – user2460978 Feb 10 '14 at 07:42
  • It's important to note that even when a copy constructor is used, a struct could contain a private pointer, and behave as though members of the pointed-to object were members of its own. In such case, the structure's copy constructor could make the copy operation behave with either reference or value semantics. – supercat Feb 10 '14 at 16:32
2

You can use references or pointers to avoid copying and having a name to relate to.

vector<MyStruct> v;

for (int i = 0; i < v.size(); ++i) {
    MyStruct& s = v[i];
    doSomething(s);
}

However since you use a vector for your container, using iterators might be a good idea. doSomething should take argument by const ref though otherwise, you'll still copy to pass argument to it.

vector<MyStruct> v;

for (vector<MyStruct>::iterator it = v.begin(); it != v.end(); ++it) {
    doSomething(*it);
}
Eric Fortin
  • 7,323
  • 2
  • 23
  • 31
2

In your examples, you are creating copies. However not all uses of operator '=' will result in a copy. C++11 allows for 'move construction' or 'move assignment' in which case you aren't actually copying the data; instead, you're just (hopefully) making a high-speed move from one structure to another. (Naturally, what it ACTUALLY does is entirely dependent upon how the move constructor or move assignment operator is implemented, but that's the intent.)

For example:

  std::vector<int> foo(); // returns a long vector

  std::vector<int> myVector = std::move(foo());

Will cause a MOVE construction, which hopefully just performs a very efficient re-pointing of the memory in the new myVector object, meaning that you don't have to copy the huge amount of data.

Don't forget, however, about the return-value optimization, as well. This was just a trivial example. RVO is actually superior to move semantics when it can be used. RVO allows the compiler to simply avoid any copying or moving at all when an object is returned, instead just using it directly on the stack where it was returned (see http://en.wikipedia.org/wiki/Return_value_optimization). No constructor is called at all.

Shirik
  • 3,321
  • 1
  • 19
  • 26