31

Currently I do the following:

// float *c_array = new float[1024];

void Foo::foo(float *c_array, size_t c_array_size) {
  //std::vector<float> cpp_array;

  cpp_array.assign(c_array, c_array + c_array_size);
  delete [] c_array;
}

How can I optimize this assigning? I would like not to perform elementwise copy but just swap pointers.

Uyghur Lives Matter
  • 18,820
  • 42
  • 108
  • 144
Dmitriy
  • 5,357
  • 8
  • 45
  • 57

5 Answers5

26

The current std::vector doesn't provide any capability or interface to take ownership of previously allocated storage. Presumably it would be too easy to pass a stack address in by accident, allowing more problems than it solved.

If you want to avoid copying into a vector, you'll either need to use vectors through your entire call chain, or do it the C way with float[] the entire time. You can't mix them. You can guaranteed that &vec[0] will be equivalent to the C-array though, fully contiguous, so using vector in the whole program may be feasible.

Mark B
  • 95,107
  • 10
  • 109
  • 188
6

Currently, the std::vector interface does not possess the capacity to move from or swap with anything except another std::vector.

Puppy
  • 144,682
  • 38
  • 256
  • 465
  • And it's unlikely to in the future. Most use cases where you would want this you can use the raw array instead, and the additional bookkeeping added to std::vector would be horrible. – Mark Ransom Apr 29 '11 at 19:29
  • I *think* that there was at least a proposal in C++0x that would allow for this to happen. However, I'm not totally sure. – Puppy Apr 29 '11 at 20:34
  • @Mark: it wouldn't require additional bookkeeping, really, if the vector takes ownership of the memory. But there are so many ways such a feature could be used wrong, I'm sure your assessment of the likeliness is accurate. – Dennis Zickefoose Apr 29 '11 at 22:22
5

The only way to do it would be to create a custom allocator.

  1. Write an allocator class that you can initialise with your array.

  2. Instantiate the vector with the allocator as an argument.

Michael J
  • 7,631
  • 2
  • 24
  • 30
  • This isn't as useful as you might think, since the vector will overwrite any pre-existing data. – Dennis Zickefoose Apr 29 '11 at 22:15
  • @geotavros -- I'll have to dig out a manual. I haven't written a custom allocator for ages. I'm not sure if I can work around the problem that Dennis points out. Possibly not, but I'll try to find time to look at it some time soon. – Michael J May 03 '11 at 18:14
1

Unlikely it's possible - it's quite dangerous, because std::vector doesn't know how the memory was allocated and how it should be freed.

If it's possible, you may replace original allocation with creation of std::vector of correct size. It uses contiguous memory area, so it can replace manually allocated buffer.

maxim1000
  • 6,297
  • 1
  • 23
  • 19
0

It is possible with the use of a custom allocator. I checked on godbolt.org with clang and gcc. To me, it looks a bit of an ugly hack – but it works at least as a proof of concept.

Of course, you have to take care of the lifetime of the array for yourself.

#include <vector>
#include <iostream>

// custom allocator
template <typename T>
class my_allocator {
  private:
    T* const addr;
  public:
    template <typename U>
    struct rebind {
      typedef my_allocator<U> other;
    };

    //provide the required no-throw constructors / destructors:
    constexpr my_allocator(T* addr_) : addr(addr_) { };
    constexpr my_allocator(const my_allocator<T>& rhs) : addr(rhs.addr) { };
    template <typename U>
    my_allocator(const my_allocator<U>& rhs, T* addr_) : addr(addr_) { };
    ~my_allocator()  { };

    //import the required typedefs:
    using value_type=T;
    using pointer=T*;
    using reference=T&;
    using const_pointer=const T*;
    using const_reference=const T&;
    using size_type=size_t;
    using difference_type=ptrdiff_t;

  constexpr pointer   allocate(size_type n, const void * = 0) {
              pointer t=addr;
              std::cout
              << "  used my_allocator to allocate   at address "
              << t << " (+)" << std::endl;
              return addr;
            }

    constexpr void      deallocate(void* p, size_type) {
        if (p) {
        std::cout
        << "  used my_allocator to deallocate at address "
        << p << " (-)" << 
        std::endl;
        } 
    }

    template< class U, class... Args >
    void construct( U* p, Args&&... args ) {
        // avoids initialisation of the elements.
        std::cout << "Contruct called" << std::endl;
    }

};

// helper function for easy useage
template<typename T>
const std::vector<T, my_allocator<T> > CreateVectorFromArray(T* array, int size) {
    const my_allocator<T> alloc=my_allocator<T>(array);
    std::vector<int, my_allocator<int> > vecAll(size, my_allocator<int>(array));
    return vecAll;
}

template<typename T>
using element_type_t = std::remove_reference_t<decltype(*std::begin(std::declval<T&>()))>;

template<typename AR>
auto CreateVectorFromArrayAr(AR& array) {
    using T=element_type_t<AR>;
    const my_allocator<T> alloc=my_allocator<T>(array);
    std::vector<T, my_allocator<T> > vecAll(sizeof(array)/sizeof(array[0]), my_allocator<T>(array));
    return vecAll;
}

int main() {
    int array[]={0,1,2,3,4,5,6,7,8,9};
    std::cout << "Array:  " << &array[0] << " " << array[0]  << " " << array[1]<< " " << array[2] << std::endl;

    auto vecAll=CreateVectorFromArray(array, sizeof(array)/sizeof(array[0]));
    auto vec3=CreateVectorFromArrayAr(array);


    std::cout << "Vector: " << &vecAll[0] << " " << vecAll[0] << " " << vecAll[1]<< " " << vecAll[2] << std::endl;
    std::cout << "Array:  " << &array[0] << " " << array[0]  << " " << array[1] << " " << array[2] << std::endl;
    std::cout << "vec3:   " << &vec3[0] << " " << vec3[0]  << " " << vec3[1] << " " << vec3[2] << std::endl;
    std::cout << "sizeof(vecAll)=" << sizeof(vecAll) << std::endl;
    std::cout << "sizeof(void*)=" << sizeof(void*) << std::endl;
    return 0;
}
Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
Bil_Bomba
  • 183
  • 1
  • 8