简体   繁体   中英

Self-contained, STL-compatible implementation of std::vector

The implementation of std::vector that ships with Visual Studio 2010 and earlier versions has a well known particularity: the resize method has the following signature (C++03-compliant):

void resize(size_type new_size, value_type value);

instead of the C++11-compliant signature that's been used by the majority of other STL implementations (like gcc's STL or STLport) long before C++11:

void resize(size_type new_size, const value_type& value);

The problem with the first variant is that, in some situations, it will fail to compile if value_type has an alignment specification:

struct __declspec(align(64)) S { ... };
std::vector<S> v;  // error C2719: '_Val': formal parameter with __declspec(align('64')) won't be aligned

This is a well known problem with no satisfactory workaround apart from using a different implementation of std::vector .

I'm looking for a well-written, well-tested, self-contained and STL-compatible implementation of std::vector with a MIT-style licence that I could drop into my project as a container of choice for aligned types.

I considered extracting it from STLport or gcc's STL but, being fully standard-compliant, they're both large with many non-trivial dependencies.

(I would be perfectly happy with an implementation of a reasonable subset of std::vector that would only support push_back , clear , capacity , size , reserve , resize , swap and array indexing.)

Any ideas?

The guys behind the Eigen library seem to have found a nice workaround for the problem of storing "overly-aligned types" ( as Stephan T. Lavavej call them ) into a std::vector as implemented in Visual Studio's STL.

Their implementation seems unnecessary complicated (check the sources here and here ) but the main idea is to encapsulate the type that goes into the std::vector with a thin wrapper:

#include <vector>

template <typename T>
struct wrapper : public T
{
    wrapper() {}
    wrapper(const T& rhs) : T(rhs) {}
};

struct __declspec(align(64)) S
{
    float x, y, z, w;
};

int main()
{
    std::vector< wrapper<S> > v;  // OK, no C2719 error
    return 0;
}

About the implementation in Eigen, I must admit I don't quite understand

  • why they need Eigen::aligned_allocator_indirection ,
  • why they need to make an exception for arithmetic types in EIGEN_WORKAROUND_MSVC_STL_SUPPORT ,
  • why they need to define all these constructors and operators in Eigen::workaround_msvc_stl_support ,
  • or why they need to redefine resize in their partial specialization of std::vector for the Eigen::aligned_allocator_indirection allocator...

Clues welcome. The point is, this trick works perfectly (as far as I can tell) and I don't see anything wrong with it, apart maybe from the slight inelegance.

The easiest (and best, imho) option would be to provide a free function as an extension to the vector interface, which does the right thing. The functions you need to implement resize are all available from the public interface of std::vector :

#include <vector>

template<class T, class Alloc>
void resize(std::vector<T, Alloc>& v,
    typename std::vector<T, Alloc>::size_type new_size, T const& val)
{
  if (v.size() < new_size)
      v.insert(v.end(), new_size - v.size(), val);
  else if (new_size < v.size())
      v.erase(v.begin() + new_size, v.end());
}

And for consistency also the single argument version:

template<class T, class Alloc>
void resize(std::vector<T, Alloc>& v,
    typename std::vector<T, Alloc>::size_type new_size)
{
    v.resize(new_size); // simply forward
}

If you, however, just want to drop-in the new vector and never worry about free or member function, another option is to simply subclass std::vector :

#include <vector>
#include <memory>

template<class T, class Alloc = std::allocator<T>>
class myvector
  : public std::vector<T, Alloc>
{
  typedef std::vector<T, Alloc> base;
public:
  typedef typename base::size_type size_type;

  void resize(size_type new_size){
    base::resize(new_size);
  }

  void resize(size_type new_size, T const& val){
    if (this->size() < new_size)
        this->insert(this->end(), new_size - this->size(), val);
    else if (new_size < this->size())
        this->erase(this->begin() + new_size, this->end());
  }
};

Note that I also provided the single argument version of resize , since the two argument version would hide all base-class versions. Also note that I needed to prefix all member function calls with this-> , since they are dependent on the base class std::vector , and as such on the template arguments.

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