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.