0

I have a question about lifetime of onstack created arrays and about transforming them to c++ vectors. For ex I have two functions:

void getVector()
{ 
    auto myVector = createVectorFromArray();
}

vector<float> createVectorFromArray()
{
    float myArr[10];
    readDataFromSomewhere(myArr, 10); //some read data function with signature void(float*, size_t)
    vector<float> resVector;
    resVector.assign(myArr, myArr+10);
    return resVector;
}

As I understand array myArr will be killed as soon as we leave function createVectorFromArray. So iterators of vector myVector in function getVector will point to nowhere. Am I right or this works in another way? And how should I better make vector from array to return it from function in this situation?

Thank you!

5
  • en.cppreference.com/w/cpp/container/vector/assign Commented Aug 31, 2016 at 8:24
  • 1
    You can't invalidate elements of a std::vector that simply, unless they are pointers. Commented Aug 31, 2016 at 8:24
  • Have you had problems doing this? It seems really easy to just compile it and test it out. Commented Aug 31, 2016 at 8:30
  • @LogicStuff thank you! as the saying goes, rtfm..... Commented Aug 31, 2016 at 8:30
  • @sokkyoku, yep, you're right. I should check it before asking such a simple q( Commented Aug 31, 2016 at 8:36

2 Answers 2

3

You can rewrite createVectorFromArray like that and you don't have anymore trouble with assigning an array to vector:

vector<float> createVectorFromArray()
{
    vector<float> resVector(10);

    readDataFromSomewhere(&resVector[0], resVector.size());

    return resVector;
}

std::vector is contiguous memory container, so you can use it as interface with plain old C functions

Sign up to request clarification or add additional context in comments.

33 Comments

@Nidhoegger in the current implementation, the floats will be uninitialised. This answer is correct and idiomatic. You're wrong to downvote it.
@Nidhoegger you're wrong to say that. It's absolutely optimal.
@sameerkn &vector[0] does not break encapsulation. A vector's elements are guaranteed by the standard to be contiguous. This use case is idiomatic.
@Garf365 the vector is returned efficiently by RVO. There is no overhead.
@Nidhoegger RVO is ubiquitous. As of c++17 it is mandated by the standard. You can rely on it.
|
2

This answer is designed to discuss the various approaches, explain them, and put them into context.

option 1: the native to vector copy:

vector<float> createVectorFromArray()
{
    // data copied once
    float myArr[10];
    readDataFromSomewhere(myArr, 10);

    // vectors are lightweight handles. zero-cost construction
    // since the optimiser will see that the next statement assigns to
    // the vector
    vector<float> resVector;

    //
    // data copied twice
    //
    resVector.assign(myArr, myArr+10);

    //
    // it's almost guaranteed that RVO will elide this copy. As of c++17
    // it's a guarantee. Returning the vector is fine
    return resVector;
}

Problems:

  1. data copied twice
  2. vector will require memory allocation

option 2: use the vector directly

vector<float> createVectorFromArray()
{
    // Will allocate space and
    // default-initialise the floats (i.e. they won't be initialised)
    vector<float> resVector(10);

    //
    // one data copy. perfectly safe and idiomatic.
    // std::addressof expressed intent clearly.
    //

    readDataFromSomewhere(std::addressof(resVector[0]), resVector.size());

    //
    // it's almost guaranteed that RVO will elide this copy. As of c++17
    // it's a guarantee. Returning the vector is efficient.
    return resVector;
}

Problems:

  1. vector will require memory allocation

Better...

option 3 : re-use an existing vector

void createVectorFromArray(vector<float>& resVector)
{
    //
    // if resVector has been used before and has a capacity() of 10
    // or more, no memory allocation necessary
    //
    resVector.resize(10);

    // one copy of data
    readDataFromSomewhere(std::addressof(resVector[0]), resVector.size());
}

Problems:

  1. perhaps not quite to easy to use the reference interface.

How would I choose between option 2 and 3?

Option 2 is more readable (IMHO) but would be expensive if used in a loop. So for a one-off, I'd go for that.

If I'm reading data into a buffer in a loop, I'd probably want to avoid un-necessary memory allocations. So re-using the vector's capacity would be a wise move.

e.g.

std::vector<float> buf;
while (thereIsMoreData())
{
    createVectorFromArray(buf);   // option 3
    useTheData(buf);
    // data no longer needed, so we can re-use the vector
}

the alternative:

while (thereIsMoreData())
{
    // the passing of the vector is efficient, particularly as of c++11
    // however, we still suffer one memory allocation per loop.
    // probably undesirable in a high performance environment.
    useTheData(createVectorFromArray());   // option 2
}

Finally...

option 4:

Provide both. Allow the user the performant approach or the 'readable' approach as he/she wishes

void createVectorFromArray(vector<float>& resVector)
{
    //
    // if resVector has been used before and has a capacity() of 10
    // or more, no memory allocation necessary
    //
    resVector.resize(10);

    // one copy of data
    readDataFromSomewhere(std::addressof(resVector[0]), resVector.size());
}

// overload
std::vector<float> createVectorFromArray()
{
    std::vector<float> result;
    createVectorFromArray(result);
    return result;
}

2 Comments

Can you add some use case, specially for 2 last options ? Where it better to use one rather than other ?
@Garf365 sure. basically in loops. You don't want to have to reallocate buffers all the time

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.