Returning a std::vector from a Function

When you return a std::vector from a function, is the compiler clever enough to use the vector’s Move Constructor and Move Assignment function to transfer ownership of the data to the receiving function?

#include <vector>
using ByteVector = std::vector<uint8_t>;

ByteVector getVector();

void setup() {
  // Get vector of values
  ByteVector values = getVector();

  //
  //  Use values from the vector
  //

  // The vector's lifetime ends here. Dynamic storage space returned to the heap;
}

ByteVector getVector(){
  ByteVector v;

  // Put values in the vector

  // Return the vector
  return v;
}

void loop() {
}

Or must I use std::move() to avoid unnecessary copying?

#include <vector>
using ByteVector = std::vector<uint8_t>;

ByteVector getVector();

void setup() {
  // Get vector of values
  ByteVector values = getVector();

  //
  //  Use values from the vector
  //

  // The vector's lifetime ends here. Dynamic storage space returned to the heap;
}

ByteVector getVector() {
  ByteVector v;

  // Put values in the vector

  // Return the vector
  return std::move(v);
}

void loop() {
}

Same question for a function returning a struct that contains a std::vector:

#include <vector>
using ByteVector = std::vector<uint8_t>;

struct MyStruct {
  float floatValue;
  ByteVector theVector;
};

MyStruct getStruct();

void setup() {
  // Get vector of values
  MyStruct values = getStruct();

  //
  //  Use values from the struct
  //

  // The struct's lifetime ends here. Dynamic storage space returned to the heap;
}

MyStruct getStruct(){
  MyStruct s;

  // Put values in the struct;

  // Return the struct
  return s;
}

void loop() {
}

Even better: in many cases, you may not even get a move constructor call, and the returned value is constructed in-place (e.g. in the stack frame of the caller).

See Copy elision - cppreference.com for details.

You should not move the returned value, that may actually result in worse performance. (If you return a struct or a tuple, you may want to move the members into the struct/tuple, though.)

OK, thanks. So for the vector case just:

  // Return the vector
  return v;

And for the struct case create custom Move operations??

#include <vector>
using ByteVector = std::vector<uint8_t>;

struct MyStruct {
  float floatValue;
  ByteVector theVector;

  MyStruct(MyStruct &&other) {
    floatValue = other.floatValue;
    theVector = std::move(theVector);
  }

  MyStruct(MyStruct &other) {
    floatValue = other.floatValue;
    theVector = theVector;
  }

  MyStruct &operator=(MyStruct &&other) {
    floatValue = other.floatValue;
    theVector = std::move(theVector);
    return *this;
  }

  MyStruct &operator=(MyStruct &other) {
    floatValue = other.floatValue;
    theVector = theVector;
    return *this;
  }

  MyStruct() = default;
  ~MyStruct() = default;
};

No, you don't need custom move operations for the struct.

If you're just returning an instance of your struct, don't move anything.

MyStruct getStruct() {
  MyStruct m;
  // ...
  return m; // no move
}

But if you already have a vector and you want to wrap it in a struct before returning, you should move the vector into the struct.

MyStruct getStruct() {
  std::vector v { /* ... */ };
  // ...
  return {3.14f, std::move(v)}; // move vector into struct
}
1 Like

The code's structure may be such that I can populate the vector in situ:

MyStruct getStruct() {
  MyStruct s;

  // Put values in the struct;
  s.theVector.push_back(0xC1);
  s.theVector.push_back(0xC2);
  s.floatValue = 3.14;

  // Return the struct
  return s;
}

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.