4

I receive an array as a pointer from a function and want to initialize a QVector from that.

For now I do it like this:

void foo(double* receivedArray, size_t size)
{
    QVector<double> vec(size);

    std::copy(receivedArray, receivedArray + size, std::begin(vec));
}

Would it be equally possible to do this:

void foo(double* receivedArray, size_t size)
{
    QVector<double> vec(size);

    vec.data() = receivedArray;
}

Would this break some kind of Qt mechanism that I am not aware of?

FreddyKay
  • 275
  • 1
  • 4
  • 13
  • Not an answer, but it would be easier and more fool-proof if `foo` would receive a std::vector of doubles instead of a pointer and size, e.g. `void foo(const std::vector& receivedArray) { ... }`. In that case all you need would be `QVector::fromStdVector`. – Elijan9 Aug 12 '16 at 13:48
  • Agreed, but this is not up to me. That is actually the point of the QVector. I have to live with receiving a float* with size_t as this is coming from another API and want to use mor advanced types otherwise in my program. – FreddyKay Aug 12 '16 at 14:18

3 Answers3

4

The first one does unnecessary work, initializing the vector with default-constructed doubles before filling it. Unfortunately, QVector lacks a ranged-insertion, so you must resort to algorithms:

void foo(double* receivedArray, size_t size)
{
    QVector<double> vec;
    vec.reserve(size); // warning: size_t->int cast

    std::copy(receivedArray, receivedArray + size, std::back_inserter(vec));
}

The second version does not even compile, as data() returns a T *, which is a rvalue that you can't put on the left side of an assignment.

peppe
  • 21,934
  • 4
  • 55
  • 70
  • 1
    I had a weird issue with reserve and back_inserter. I must do the above function a lot of times with rather large arrays in the range of GB. I made the observation that QVector::reserve followed by QVector::resize, and then a simple copy via, iterators behaved faster (approximately 50%) than the back_inserter version. Do you remember a similiar behaviour? – FreddyKay Aug 12 '16 at 14:20
  • 1
    @FreddyKay Yes, using your version is probably faster in this specific case for two reasons: 1) Initializing an `std::vector` with `0` is fast because you simply need to do a `std::memset(data(), 0, size() * sizeof(double))` (on most architecture) and 2) Copying trivial type is also very fast with `std::memcpy(data(), src, size() * sizeof(double))`. Compilers know how to optimize `std::vector` and `std::copy` for trivially type such as `double`, using a `std::back_inserter` is on the other hand really hard to optimize. – Holt Aug 12 '16 at 14:25
  • @FreddyKay Also note that `QVector()` probably pre-allocate some capacity, so if you reserve afterward, you need to deallocate and then reallocate. – Holt Aug 12 '16 at 14:30
  • Since there's COW involved, `QVector()` does not actually preallocate anything. Then, yes, you need to measure what you're doing; `back_inserter` is surely the more general solution. – peppe Aug 12 '16 at 15:26
1

QVector::data does not return a reference to the underlying pointer, so you cannot assign to vec.data() (it is not an lvalue, it will not even compile):

template <typename T>
struct Vector {
    T* data_;

    T* nref_data () { return data_; }
    T* &ref_data () { return data_; }
};

Vector<int> vec;
vec.ref_data() = new int[100]; // Ok, Vector<int>::ref_data returns a reference
vec.nref_data() = new int[100]; // Nok, Vector<int>::nref_data does not return a reference
Holt
  • 36,600
  • 7
  • 92
  • 139
0

Another solution:

void foo(double* receivedArray, size_t size)
{
   QVector<double> vec(receivedArray,receivedArray+size);
}

see also: http://cpp.sh/7jfi6