简体   繁体   中英

Pointer arithmetic with std::vector

Previously, I was working with some code in a library that had the interface

void f( T* x );
void g( T* x );

where

  • f would fill the first m entries of x with some values (overwriting whatever is in x )
  • g would fill the first n entries of x with some values (overwriting whatever is in x )

I wanted to concatenate these two values, so I did this

void concat( T* x ){
    f(x);
    x += m; 
    g(x);
    x += n; 
    ...
}

Actually, there were about 10 such functions that I concatenated using pointer arithmetic like this.

Now we are trying to use a different library for the same purpose. However, the new library has the interface

void f_new( std::vector<T> & x );
void g_new( std::vector<T> & x );

where again, these functions fill the first m and n elements of x , respectively, (overwriting whatever is currently in x ). Furthermore, I must create a new concat function with the signature

void concat_new( std::vector<T> & x  ){
    // TODO
    ...
}

What is the most efficient way of achieving the previous result with vectors? The only way I can figure out how to do this is by copying data between calls.

NOTE: I can't modify the signatures for concat_new, f_new, or g_new

There is no efficient way of doing this.

A good library should use iterators. If it does not you will have to copy the elements.

However: You can optimize a bit by reserving space for the target vector if you know the final size.

void concat( std::vector<T>& x ){
    x.reserve(m+n+...);
    f(x);

    std::vector<T> buffer;
    buffer.reserve(std::max({n, ...}));

    g(buffer);
    x.insert(x.end(), buffer.begin(), buffer.end());

    ...
}

by reusing buffer you can at least skip the reallocations.


IF you could change the signature of f then change it to

void f(std::vector<T>::iterator begin, std::vector<T>::iterator end) {
  ... // (should use 'end' at least to check the target size)
}

You can always use a wrapper for backward compatibility:

void f(std::vector<T>& x)
{ 
  f(x.begin(), x.end());
}

Then use

void concat( std::vector<T>& x) {
  assert(x.size() >= m+n);
  f(x.begin(), x.begin() + m);
  g(x.begin() + m, x.begin() + m + n);
}

But make sure x is large enough!

Carry on using the old f and g , they are more applicable to you than f_new and g_new .

void concat_new( std::vector<T> & x  )
{
    auto it = x.data();
    f(it);
    it += m; 
    g(it);
    it += n; 
    ...
}

Petition the author of the library to adopt @bartop's signatures (as an overload, perhaps)

I think that instead of passing whole std::vectors, the vector's iterator would be sufficient for Your task. It gives You exactly the same functionallity as the pointer and works with vector. I'd go with something like this:

void concat_new( std::vector<T> & x  ){
    auto it = x.begin();
    f(x);
    it += m; 
    g(x);
    it += n; 
    ...
}

With it, f and g would be like this:

void f_new( std::vector<T>::iterator x );
void g_new( std::vector<T>::iterator x );

The thing that is great about it - You would not need any further changes in code if You did no evil pointer sorcery in the code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM