I'm almost done with the implementation of a std::vector type of vector (hopefully) but I'm having a little bug in the code and I can't seem to find where. Basically when I build Vector and use push_back, the vector automatically allocates new memory for more elements (specifically twice the size of the vector) but when doing so, the "extra space" is initialized to 0's and I don't know why.
Here is my code:
Vector.h
#include <memory>
#include <cstddef>
template <class T>
class Vec{
public:
typedef T* iterator;
typedef const T* const_iterator;
typedef size_t size_type;
typedef T value_type;
Vec(){create();}
Vec(size_type n, const T& val = T()) {create(n, val);}
~Vec() {uncreate();}
//copy constructor
Vec(const Vec& v) {create(v.begin(), v.end());}
//assignment operator
Vec& operator=(const Vec&);
size_type size() const {return limit - data;}
//index operators
T& operator[](size_type i) {return data[i];}
const T& operator[](size_type i) const {return data[i];}
iterator begin() {return data;}
const_iterator begin() const {return data;}
iterator end() {return limit;}
const_iterator end() const {return limit;}
void push_back(const T&);
private:
iterator data; //1st element
iterator avail; //one past last constructed element
iterator limit; //one past last available element
//Memory management
std::allocator<T> alloc;
void create();
void create(size_type, const T&);
void create(const_iterator, const_iterator);
void uncreate();
void grow();
void unchecked_append(const T&);
};
template <class T>
void Vec<T>::push_back(const T& val){
if(avail == limit)
grow();
unchecked_append(val);
}
template <class T>
Vec<T>& Vec<T>::operator=(const Vec& rhs){
//self-assign
if(&rhs != this){
uncreate;
create(rhs.begin(), rhs.end());
}
return *this;
}
// Empty Vector, pointers to 0
template <class T> void Vec<T>::create(){
data = avail = limit = 0;
}
// Allocate memory for (size)
template <class T> void Vec<T>::create(size_type n, const T& val){
data = alloc.allocate(n); // returns pointer to first element
limit = avail = data +n;
std::uninitialized_fill(data, limit, val);
}
template <class T> void Vec<T>::create(const_iterator i, const_iterator j){
data = alloc.allocate(j-i);
limit = avail =std::uninitialized_copy(i, j, data);
}
template <class T> void Vec<T>::uncreate(){
if(data){
iterator it = avail;
while(it != data)
alloc.destroy(--it);
// Free space
alloc.deallocate(data, limit - data);
}
// Empty Vector
data = limit = avail = 0;
}
template <class T> void Vec<T>::grow(){
// Allocate twice the space we had
size_type new_size = std::max(2 * (limit - data), ptrdiff_t(1));
// Allocate new space and copy to new space
iterator new_data = alloc.allocate(new_size);
iterator new_avail = std::uninitialized_copy(data, avail, new_data);
// Return old space used
uncreate();
// Reset pointers to point to new space
data = new_data;
avail = new_avail;
limit = data + new_size;
}
template <class T> void Vec<T>::unchecked_append(const T& val){
alloc.construct(avail++, val);
}
And this is how I created the vector
main.cpp
#include "vector.h"
#incude <iostream>
using namespace std;
int main(){
Vec<int> v1;
v1.push_back(12);
v1.push_back(9);
v1.push_back(74);
v1.push_back(22);
Vec<int> v2 = v1;
v2.push_back(70); //After doing this the vector is (12, 9, 74, 22, 70, 0, 0, 0)
for(auto e: v2) cout << e << " ";
cout << endl;
}
Thanks for any help and also any feedback on the actual code is appreciated. :)
The problem is (i do not know if it is really problem)
template <class T> void Vec<T>::grow(){
// Allocate twice the space we had
size_type new_size = std::max(2 * (limit - data), ptrdiff_t(1));
....
....
}
No need to do any vector copy to reproduce your problem.
The problem can be seen even in first vector after inserting 5th elemnt
v1.push_back(12);
v1.push_back(9);
v1.push_back(74);
v1.push_back(22);
v1.push_back(22); //5th elemnt.
now printing the size;
cout<<v1.size()<<endl; - it says 8.
Even the std vector, after resize(), it is normal behavior to print zeros for the empty elements.
Look at this example:
http://en.cppreference.com/w/cpp/container/vector/resize
if you do not want additional zeros, you have to tweak your ":new_size" calculation.
How would I go about making a function that uses braces like if/for/while statements? I'm referring to this as a 'keyword statement' because I don't know what else to call it.
Meaning, for example, if I wanted to make a 'repeat' function:
repeat(3)
{
//do something
}
I guess a better question is, is this possible? If so, how would one go about doing this?
Don't do that [#define repeat] - don't try to change the syntax of the programming language you're using. That will make your code far less readable for anyone else.
You might define a range similar to a python range:
// Range
// =====
#include <iterator>
#include <utility>
template<typename T>
class Range
{
public:
typedef T value_type;
public:
class iterator
{
public:
typedef typename std::forward_iterator_tag iterator_category;
typedef typename std::size_t size_type;
typedef typename std::ptrdiff_t difference_type;
typedef T value_type;
typedef const T& reference;
typedef const T* pointer;
public:
iterator(const T& value) noexcept
: m_value(value)
{}
reference operator * () const noexcept { return m_value; }
pointer operator -> () const noexcept { return &m_value; }
iterator& operator ++ () noexcept { ++m_value; return *this; }
friend bool operator == (const iterator & a, const iterator b) noexcept {
return a.m_value == b.m_value;
}
friend bool operator != (const iterator & a, const iterator b) noexcept {
return a.m_value != b.m_value;
}
private:
T m_value;
};
public:
Range(const T& first, const T& last) noexcept
: m_first(first), m_last(last)
{}
Range(T&& first, T&& last) noexcept
: m_first(std::move(first)), m_last(std::move(last))
{}
Range(Range&& other) noexcept
: m_first(std::move(other.m_first)),
m_last(std::move(other.m_last))
{}
Range& operator = (Range&& other) noexcept {
m_first = std::move(other.m_first);
m_last = std::move(other.m_last);
return *this;
}
iterator begin() const noexcept { return m_first; }
iterator end() const noexcept { return m_last; }
private:
T m_first;
T m_last;
};
template<typename T>
inline Range<T> range(T&& first, T&& last) noexcept {
return Range<T>(std::move(first), std::move(last));
}
// Test
// ====
#include <iostream>
int main() {
for(auto i : range(0, 3))
std::cout << i << '\n';
}
A more sophisticated implementation would consider containers and iterators, too.
You could define a macro taking 1 argument:
#define repeat(COUNT) \
for (unsigned int i = 0; i < (COUNT); ++i)
and leave the brakets empty after it, the preprocessor will expand the following example:
repeat(3)
{
//do something
}
into:
for (unsigned int i = 0; i < (3); ++i)
{
//do something
}
Demo
This question already has answers here:
Why can templates only be implemented in the header file?
(17 answers)
Closed 9 years ago.
I am getting this error when compiling it with: g++ main.cpp Vec.cpp -Wall -o main -I.
/tmp/cciqbEQJ.o: In function `main':
main.cpp:(.text+0x8b): undefined reference to `Vec<double>::Vec()'
main.cpp:(.text+0x9b): undefined reference to `Vec<double>::~Vec()'
collect2: ld returned 1 exit status
make: *** [all] Error 1
I don't understand because I have done a lot of multiple-source file programs before and I never got this error where the constructor is not found. It seems like the compiler is not able to dynamically bind the template code to the instantiation of the template. Also, I have put a macro guard on the .h file but it is not shown below.
The source codes are below:
Vec.cpp
#include "Vec.h"
using namespace std;
template<class T>
Vec<T>::Vec() {
create();
}
template<class T>
Vec<T>::Vec( size_type n, const value_type& t ){
create(n,t);
}
template<class T>
Vec<T>::Vec(const Vec& v)
{
create(v.begin(), v.end());
}
template<class T>
Vec<T>::~Vec(){
uncreate();
}
template<class T>
void Vec<T>::create()
{
data = avail = limit = 0;
}
template<class T>
void Vec<T>::create(size_type n, const T& val)
{
data = alloc.allocate(n);
limit = avail = data + n;
uninitialized_fill(data,limit, val);
}
template<class T>
void Vec<T>::create(const_iterator i, const_iterator j) {
data = alloc.allocate(j-i);
limit = avail = uninitialized_copy(i, j, data);
}
template<class T>
void Vec<T>::uncreate() {
if (data) {
iterator it = avail;
while (it != data)
alloc.destroy(--it);
alloc.deallocate(data,limit-data);
}
data = limit = avail =0;
}
template<class T> void Vec<T>::grow() {
size_type new_size = max ( 2 * (limit-data), ptrdiff_t(1));
iterator new_data = alloc.allocate(new_size);
iterator new_avail = unitialized_copy(data, avail, new_data);
uncreate();
data = new_data;
avail = new_avail;
limit = data + new_size;
}
template<class T> void Vec<T>::unchecked_append(const T& val) {
alloc.construct(avail++, val);
}
template<class T>
void Vec<T>::push_back(const T& t){
if ( avail == limit )
grow();
unchecked_append(t);
}
Vec.h
template<class T> class Vec{
public:
typedef T* iterator;
typedef const T* const_iterator;
typedef size_t size_type;
typedef T value_type;
Vec();
Vec( size_type n, const T& t=T() );
Vec(const Vec& v);
Vec& operator=(const Vec& v);
~Vec();
void push_back(const T& t);
inline size_type size() const { return limit - data; }
inline iterator begin() {return data;}
inline const_iterator begin() const { return data; }
inline iterator end() { return limit; }
inline const_iterator end() const { return limit; }
inline T& operator[](size_type i){
return data[i];
}
const T& operator[](size_type i) const { return data[i]; }
private:
iterator data;
iterator limit;
iterator avail;
//facilities for memory allocation
allocator<T> alloc;
//allocate and initialize the underlying array
void create();
void create(size_type, const T&);
void create(const_iterator, const_iterator);
//destroy the elements in the array and free the memory
void uncreate();
//support functions for push_back
void grow();
void unchecked_append(const T&);
};
main.cpp
int main(void) {
Vec<double> test;
}
In order for the compiler to generate the code, it must see both the template definition and the specific types used to for the template.
So, in main.cpp add line just #include "Vec.cpp" at the top.
Compile using
g++ main.cpp -Wall -o main -I. <-- Notice Vec.cpp removed now.
This will make sure the template declaration and implementation are together during compilation, at same time implementation is still separated from declaration.
Another alternative is to include this .cpp while at the end of Vec.h as suggested in the above commented link of SO by juanchopanza
For more details Ref :- Why can't I separate the definition of my templates class from its declaration and put it inside a .cpp file ?
Chapter 11 of Accelerated C++ covers implementing template classes, using a simplified version of the STL's vector class as an example. Exercise 11-6 wants us to add the .erase() and .clear() methods to the class, so first I copied the final code directly from the book and tried to compile, but it failed. I then moved all the function definitions into the .h file (removing the Vec<T>:: etc. stuff as necessary) and compiled just my main.cpp, which worked.
Here's all of my code:
main.cpp
#include <iostream>
#include "Vec.h"
using std::cout;
using std::endl;
int main()
{
Vec<int> v;
for (int i = 1; i < 10; ++i)
v.push_back(i);
for(Vec<int>::const_iterator iter = v.begin();
iter != v.end(); ++iter)
cout << *iter << endl;
return 0;
}
Vec.h
#ifndef GUARD_Vec_h
#define GUARD_Vec_h
#include <cstddef>
#include <memory>
template <class T> class Vec {
public:
// member variables
typedef T* iterator;
typedef const T* const_iterator;
typedef std::size_t size_type;
typedef T value_type;
typedef T& reference;
typedef const T& const_reference;
// constructors + destructors
Vec() { create(); }
explicit Vec(size_type n, const T& t = T()) { create(n, t); }
Vec(const Vec& v) { create(v.begin(), v.end()); }
~Vec() { uncreate(); }
// methods
T& operator[](size_type i) { return data[i]; }
const T& operator[](size_type i) const { return data[i]; }
void push_back(const T& t) {
if (avail == limit)
grow();
unchecked_append(t);
}
size_type size() const { return avail - data; }
iterator begin() { return data; }
const_iterator begin() const { return data; }
iterator end() { return avail; }
const_iterator end() const { return avail; }
private:
iterator data;
iterator avail;
iterator limit;
std::allocator<T> alloc;
void create();
void create(size_type, const T&);
void create(const_iterator, const_iterator);
void uncreate();
void grow();
void unchecked_append(const T&);
};
#endif GUARD_Vec_h
Vec.cpp
#include <algorithm>
#include <cstddef>
#include <memory>
#include "Vec.h"
using std::allocator;
using std::max;
using std::uninitialized_copy;
using std::uninitialized_fill;
using std::ptrdiff_t;
template <class T> void Vec<T>::create()
{
data = avail = limit = 0;
}
template <class T> void Vec<T>::create(size_type n, const T& val)
{
data = alloc.allocate(n);
limit = avail = data + n;
uninitialized_fill(data, limit, val);
}
template <class T> void Vec<T>::create(const_iterator i, const_iterator j)
{
data = alloc.allocate(j - i);
limit = avail = uninitialized_copy(i, j, data);
}
template <class T> void Vec<T>::uncreate()
{
if (data) {
iterator it = avail;
while (it != data)
alloc.destroy(--it);
alloc.deallocate(data, limit - data);
}
data = limit = avail = 0;
}
template <class T> void Vec<T>::grow()
{
size_type new_size = max(2 * (limit - data), ptrdiff_t(1));
iterator new_data = alloc.allocate(new_size);
iterator new_avail = uninitialized_copy(data, avail, new_data);
uncreate();
data = new_data;
avail = new_avail;
limit = data + new_size;
}
template <class T> void Vec<T>::unchecked_append(const T& val)
{
alloc.construct(avail++, val);
}
Why doesn't this compile?
The problem is with any and all of the template functions. When you write a Template function, the compiler instantiates the template--that is, it creates a specific version of the function with the types you need to run the code. So when you call
Vec<int> v;
The compiler makes code for a Vec and any function of that type you call. The compiler needs to have access to the templated function definitions while making the code for the main file, because it has to know what kind of code to make for the object file before it goes to link the other files.
I have a class that is using boost::aligned storage to statically allocate memory within the object, then initialize the objects at a later time. However, a crash in one test program appears to show the members' destructors being called on the initialized object, a std::set in this case, when I attempt to return the object after a function call. Then, only the data appears to be copied over on assignment to the new object at the return site, as if the object was a POD type. This means that the next time I attempt to access the assigned object, it fails with a memory access error.
More specifically, I'm implementing a class called StaticVector, a subset of std::vector which attempts to closely match Boost.Array plus size tracking and a compile time fixed capacity.
My test program benchStaticVector runs as follows:
calls a function that adds a randomly filled std::set to a StaticVector
returns it to a local variable
the destructor is being called on the return, but for some reason the returned object isn't copying correctly
when the local variable goes out of scope free gets called on uninitialized memory
Crash
What is the source of this crash, and how do I resolve it?
The full code and a convenient CMakeLists.txt file can be found at:
https://github.com/ahundt/Boost.StaticVector
benchStaticVector.cpp:
// benchmark based on: http://cpp-next.com/archive/2010/10/howards-stl-move-semantics-benchmark/
#include "StaticVector.hpp"
#include <vector>
#include <iostream>
#include <time.h>
#include <set>
#include <algorithm>
#include <exception>
const unsigned N = 3;
extern bool some_test;
template<typename T>
T get_set(std::size_t)
{
T s;
for (std::size_t i = 0; i < N; ++i)
while (!s.insert(std::rand()).second)
;
if (some_test)
return s;
return T();
}
template<typename T>
T generate()
{
T v;
for (std::size_t i = 0; i < N; ++i)
v.push_back(get_set<typename T::value_type>(i));
if (some_test)
return v;
return T();
}
template<typename T>
float time_it()
{
clock_t t1, t2, t3, t4;
clock_t t0 = clock();
{
T v(generate<T>());
}
return (float)((t4-t0)/(double)CLOCKS_PER_SEC);
}
int main()
{
try {
std::cout << "N = " << N << "\n\n";
std::cout << "StaticVector Benchmark:\n";
float t = time_it<boost::StaticVector<std::set<std::size_t>,N > >();
std::cout << "Total time = " << t << "\n\n";
std::cout << "Vector: Benchmark\n";
t = time_it<std::vector<std::set<std::size_t> > >();
std::cout << "Total time = " << t << '\n';
}catch(std::exception e){
std::cout << e.what();
}
}
bool some_test = true;
Here is the class in StaticVector.hpp:
/**
* #file StaticVector.hpp
* #date Feb 23, 2011
* #brief Vector style class with fixed capacity.
*
* The following code declares class StaticVector,
* an STL container (as wrapper) for a statically allocated vector with a constant size limit.
* StaticVector is not accepted as part of boost.
*
* (C) Carnegie Mellon University 2011
* #author Andrew Hundt <ahundt#cmu.edu>
*
* based on boost::array
* The original author site of boost::array is at: http://www.josuttis.com/
* (C) Copyright Nicolai M. Josuttis 2001.
*
* Distributed under the Boost Software License, Version 1.0. (See
* accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* 09 Oct 2011 - (ath) eliminated construction of objects on initialization of StaticVector
* 23 Feb 2011 - (ath) converted to boost::StaticVector
* 28 Dec 2010 - (mtc) Added cbegin and cend (and crbegin and crend) for C++Ox compatibility.
* 10 Mar 2010 - (mtc) fill method added, matching resolution of the standard library working group.
* See <http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#776> or Trac issue #3168
* Eventually, we should remove "assign" which is now a synonym for "fill" (Marshall Clow)
* 10 Mar 2010 - added workaround for SUNCC and !STLPort [trac #3893] (Marshall Clow)
* 29 Jan 2004 - c_array() added, BOOST_NO_PRIVATE_IN_AGGREGATE removed (Nico Josuttis)
* 23 Aug 2002 - fix for Non-MSVC compilers combined with MSVC libraries.
* 05 Aug 2001 - minor update (Nico Josuttis)
* 20 Jan 2001 - STLport fix (Beman Dawes)
* 29 Sep 2000 - Initial Revision (Nico Josuttis)
*
* Jan 29, 2004
*/
#ifndef BOOST_STATIC_VECTOR_HPP
#define BOOST_STATIC_VECTOR_HPP
#include <boost/config.hpp>
#include <boost/detail/workaround.hpp>
#if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
# pragma warning(push)
# pragma warning(disable:4996) // 'std::equal': Function call with parameters that may be unsafe
# pragma warning(disable:4510) // boost::StaticVector<T,N>' : default constructor could not be generated
# pragma warning(disable:4610) // warning C4610: class 'boost::StaticVector<T,N>' can never be instantiated - user defined constructor required
#endif
#include <cstddef>
#include <stdexcept>
#include <boost/assert.hpp>
#if ((BOOST_VERSION / 100) % 1000) > 44
#include <boost/swap.hpp>
#endif
// Handles broken standard libraries better than <iterator>
#include <boost/detail/iterator.hpp>
#include <boost/throw_exception.hpp>
#include <algorithm>
// FIXES for broken compilers
#include <boost/config.hpp>
// Selection of types for internal storage
#include <boost/integer.hpp>
#include <boost/type_traits/alignment_of.hpp>
#include <boost/type_traits/aligned_storage.hpp>
#include <boost/type_traits/has_trivial_destructor.hpp>
namespace boost {
template<class T, std::size_t N>
class StaticVector {
public:
// type definitions
typedef T value_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T* iterator;
typedef const T* const_iterator;
typedef T& reference;
typedef const T& const_reference;
typedef typename boost::aligned_storage<
sizeof(T),
boost::alignment_of<T>::value
>::type aligned_storage;
typedef typename boost::uint_value_t<N>::least size_type;
typedef std::size_t max_size_type;
typedef std::ptrdiff_t difference_type;
private:
size_type m_size; // fastest type that can accomodate N
aligned_storage elems[N]; // fixed-size array of memory aligned elements of type T
public:
// iterator support
iterator begin() { return reinterpret_cast<iterator>(elems); }
const_iterator begin() const { return reinterpret_cast<const_iterator>(elems); }
const_iterator cbegin() const { return reinterpret_cast<const_iterator>(elems); }
iterator end() { return to_object(m_size); }
const_iterator end() const { return to_object(m_size); }
const_iterator cend() const { return to_object(m_size); }
// reverse iterator support
#if !defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) && !defined(BOOST_MSVC_STD_ITERATOR) && !defined(BOOST_NO_STD_ITERATOR_TRAITS)
typedef std::reverse_iterator<iterator> reverse_iterator;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
#elif defined(_MSC_VER) && (_MSC_VER == 1300) && defined(BOOST_DINKUMWARE_STDLIB) && (BOOST_DINKUMWARE_STDLIB == 310)
// workaround for broken reverse_iterator in VC7
typedef std::reverse_iterator<std::_Ptrit<value_type, difference_type, iterator,
reference, iterator, reference> > reverse_iterator;
typedef std::reverse_iterator<std::_Ptrit<value_type, difference_type, const_iterator,
const_reference, iterator, reference> > const_reverse_iterator;
#elif defined(_RWSTD_NO_CLASS_PARTIAL_SPEC)
typedef std::reverse_iterator<iterator, std::random_access_iterator_tag,
value_type, reference, iterator, difference_type> reverse_iterator;
typedef std::reverse_iterator<const_iterator, std::random_access_iterator_tag,
value_type, const_reference, const_iterator, difference_type> const_reverse_iterator;
#else
// workaround for broken reverse_iterator implementations
typedef std::reverse_iterator<iterator,T> reverse_iterator;
typedef std::reverse_iterator<const_iterator,T> const_reverse_iterator;
#endif
reverse_iterator rbegin() { return reverse_iterator(end()); }
const_reverse_iterator rbegin() const {
return const_reverse_iterator(end());
}
const_reverse_iterator crbegin() const {
return const_reverse_iterator(end());
}
reverse_iterator rend() { return reverse_iterator(begin()); }
const_reverse_iterator rend() const {
return const_reverse_iterator(begin());
}
const_reverse_iterator crend() const {
return const_reverse_iterator(begin());
}
StaticVector():m_size(0){}
StaticVector(size_type n, const_reference value):
m_size(n)
{
insert(begin(),n,value);
}
template<typename InputIterator>
StaticVector(InputIterator first, InputIterator last):
m_size(last-first)
{
capacitycheck(size());
std::copy(first,last,begin());
}
template<std::size_t SizeRHS>
StaticVector(const StaticVector<T,SizeRHS>& rhs):
m_size(rhs.size())
{
capacitycheck(rhs.size());
std::copy(rhs.begin(),rhs.end(),begin());
}
~StaticVector(){
destroy_array(::boost::has_trivial_destructor<T>());
m_size=0;
}
void push_back (const_reference x){
capacitycheck(size()+1);
new (to_object(size())) T(x);
m_size++;
}
void pop_back(){
if(!empty()){
to_object(size()-1)->~T();
m_size--;
} else {
boost::throw_exception( std::out_of_range("StaticVector<> pop called on empty container."));
}
}
iterator insert(iterator pos, const_reference x){
capacitycheck(size()+1);
std::copy_backward(pos,end(),end()+1);
*pos = x;
m_size++;
return pos;
}
void insert(iterator pos, max_size_type n, const_reference x){
capacitycheck(size()+n);
std::copy_backward(pos,end(),end()+n);
std::fill(pos,pos+n,x);
m_size+=n;
}
template <typename InputIterator>
void insert(iterator pos, InputIterator first, InputIterator last){
max_size_type n = last - first;
capacitycheck(size()+n);
std::copy_backward(pos,end(),end()+n);
std::copy(first,last,pos);
}
iterator erase(iterator pos){
rangecheck(pos-begin());
pos->~T();
std::copy(pos+1,end(),pos);
m_size--;
return pos;
}
iterator erase(iterator first, iterator last){
std::ptrdiff_t n = last-first;
if (n>0) {
rangecheck(size()-n);
for(iterator it = first; it!=last; it++){
it->~T();
}
std::copy(last,end(),first);
m_size -= n;
}
return first;
}
void clear(){
erase(begin(),end());
}
void resize(max_size_type n, const_reference t = T() ){
capacitycheck(n);
if(n - m_size > 0){
std::fill_n(end(), n-m_size, t);
} else {
erase(begin()+n,end());
}
m_size = n;
}
void reserve(max_size_type n){
capacitycheck(n);
}
// operator[]
reference operator[](max_size_type i)
{
BOOST_ASSERT( i < N || i < size() && "StaticVector<>: out of range" );
return *to_object(i);
}
const_reference operator[](max_size_type i) const
{
BOOST_ASSERT( i < N || i < size() && "StaticVector<>: out of range" );
return *to_object(i);
}
// at() with range check
reference at(max_size_type i) { rangecheck(i); return *to_object(i); }
const_reference at(max_size_type i) const { rangecheck(i); return *to_object(i); }
// front() and back()
reference front()
{
return begin();
}
const_reference front() const
{
return begin();
}
reference back()
{
return *to_object(size()-1);
}
const_reference back() const
{
return *to_object(size()-1);
}
// capacity is constant, size varies
max_size_type size() const { return m_size; }
static max_size_type capacity() { return N; }
bool empty() { return m_size == 0; }
static max_size_type max_size() { return N; }
enum { static_size = N };
// swap (note: linear complexity)
void swap (StaticVector<T,N>& y) {
#if ((BOOST_VERSION / 100) % 1000) > 44
for (size_type i = 0; i < N; ++i)
boost::swap(*to_object(i),*y.to_object(i));
boost::swap(m_size,y.m_size);
#else
std::swap_ranges(begin(),end(),y.begin());
std::swap(m_size,y.m_size);
#endif
}
// direct access to data (read-only)
const_pointer data() const { return elems; }
pointer data() { return elems; }
// use array as C array (direct read/write access to data)
pointer c_array() { return elems; }
// assignment with type conversion
template <typename T2>
StaticVector<T,N>& operator= (const StaticVector<T2,N>& rhs) {
std::copy(rhs.begin(),rhs.end(), begin());
m_size = rhs.size();
return *this;
}
// assign one value to all elements
void assign (const T& value) { fill ( value ); } // A synonym for fill
void fill (const T& value)
{
std::fill_n(begin(),size(),value);
}
// check range (may be private because it is static)
void rangecheck (max_size_type i) const {
if (i >= size()) {
std::out_of_range e("StaticVector<>: index out of range");
boost::throw_exception(e);
}
}
// check range (may be private because it is static)
void capacitycheck (max_size_type i) const {
if (i > capacity()) {
std::out_of_range e("StaticVector<>: index out of capacity");
boost::throw_exception(e);
}
}
private:
inline const_pointer to_object(size_type index) const {
return reinterpret_cast<const_pointer>(elems+index);
}
inline pointer to_object(size_type index) {
return reinterpret_cast<pointer>(elems+index);
}
// T has a trivial destructor, do nothing
inline void destroy_array(const boost::true_type&) {}
// T has a destructor, destroy each object
inline void destroy_array(const boost::false_type&) {
for(iterator first = begin(); first != end(); ++first) {
first->~T();
}
}
}; // class StaticVector
#if !defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION)
template< class T >
class StaticVector< T, 0 > {
public:
// type definitions
typedef T value_type;
typedef T* iterator;
typedef const T* const_iterator;
typedef T& reference;
typedef const T& const_reference;
typedef typename boost::aligned_storage<
sizeof(T),
boost::alignment_of<T>::value
>::type aligned_storage;
typedef typename boost::uint_value_t<0>::least size_type;
typedef std::size_t max_size_type;
typedef std::ptrdiff_t difference_type;
// iterator support
iterator begin() { return iterator( reinterpret_cast< T * >( this ) ); }
const_iterator begin() const { return const_iterator( reinterpret_cast< const T * >( this ) ); }
const_iterator cbegin() const { return const_iterator( reinterpret_cast< const T * >( this ) ); }
iterator end() { return begin(); }
const_iterator end() const { return begin(); }
const_iterator cend() const { return cbegin(); }
// reverse iterator support
#if !defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) && !defined(BOOST_MSVC_STD_ITERATOR) && !defined(BOOST_NO_STD_ITERATOR_TRAITS)
typedef std::reverse_iterator<iterator> reverse_iterator;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
#elif defined(_MSC_VER) && (_MSC_VER == 1300) && defined(BOOST_DINKUMWARE_STDLIB) && (BOOST_DINKUMWARE_STDLIB == 310)
// workaround for broken reverse_iterator in VC7
typedef std::reverse_iterator<std::_Ptrit<value_type, difference_type, iterator,
reference, iterator, reference> > reverse_iterator;
typedef std::reverse_iterator<std::_Ptrit<value_type, difference_type, const_iterator,
const_reference, iterator, reference> > const_reverse_iterator;
#elif defined(_RWSTD_NO_CLASS_PARTIAL_SPEC)
typedef std::reverse_iterator<iterator, std::random_access_iterator_tag,
value_type, reference, iterator, difference_type> reverse_iterator;
typedef std::reverse_iterator<const_iterator, std::random_access_iterator_tag,
value_type, const_reference, const_iterator, difference_type> const_reverse_iterator;
#else
// workaround for broken reverse_iterator implementations
typedef std::reverse_iterator<iterator,T> reverse_iterator;
typedef std::reverse_iterator<const_iterator,T> const_reverse_iterator;
#endif
reverse_iterator rbegin() { return reverse_iterator(end()); }
const_reverse_iterator rbegin() const {
return const_reverse_iterator(end());
}
const_reverse_iterator crbegin() const {
return const_reverse_iterator(end());
}
reverse_iterator rend() { return reverse_iterator(begin()); }
const_reverse_iterator rend() const {
return const_reverse_iterator(begin());
}
const_reverse_iterator crend() const {
return const_reverse_iterator(begin());
}
void push_back (const_reference x){
failed_rangecheck();
}
void pop_back(){
failed_rangecheck();
}
iterator insert(iterator pos, const_reference x){
return failed_rangecheck();
}
void insert(iterator pos, max_size_type n, const_reference x){
failed_rangecheck();
}
template <typename InputIterator>
void insert(iterator pos, InputIterator first, InputIterator last){
failed_rangecheck();
}
iterator erase(iterator pos){
return failed_rangecheck();
}
iterator erase(iterator first, iterator last){
return failed_rangecheck();
}
void clear(){
}
void resize(max_size_type n, const_reference t = T() ){
failed_rangecheck();
}
void reserve(size_type n){
failed_rangecheck();
}
// operator[]
reference operator[](max_size_type /*i*/)
{
return failed_rangecheck();
}
const_reference operator[](max_size_type /*i*/) const
{
return failed_rangecheck();
}
// at() with range check
reference at(max_size_type /*i*/) { return failed_rangecheck(); }
const_reference at(max_size_type /*i*/) const { return failed_rangecheck(); }
// front() and back()
reference front()
{
return failed_rangecheck();
}
const_reference front() const
{
return failed_rangecheck();
}
reference back()
{
return failed_rangecheck();
}
const_reference back() const
{
return failed_rangecheck();
}
// size is constant
static max_size_type size() { return 0; }
static bool empty() { return true; }
static max_size_type max_size() { return 0; }
enum { static_size = 0 };
void swap (StaticVector<T,0>& /*y*/) {
}
// direct access to data (read-only)
const T* data() const { return 0; }
T* data() { return 0; }
// use array as C array (direct read/write access to data)
T* c_array() { return 0; }
// assignment with type conversion
template <typename T2>
StaticVector<T,0>& operator= (const StaticVector<T2,0>& ) {
return *this;
}
// assign one value to all elements
void assign (const T& value) { fill ( value ); }
void fill (const T& ) {}
// check range (may be private because it is static)
static reference failed_rangecheck () {
std::out_of_range e("attempt to access element of an empty StaticVector");
boost::throw_exception(e);
#if defined(BOOST_NO_EXCEPTIONS) || !defined(BOOST_MSVC)
//
// We need to return something here to keep
// some compilers happy: however we will never
// actually get here....
//
static T placeholder;
return placeholder;
#endif
}
};
#endif
// comparisons
template<class T, std::size_t N>
bool operator== (const StaticVector<T,N>& x, const StaticVector<T,N>& y) {
return std::equal(x.begin(), x.end(), y.begin());
}
template<class T, std::size_t N>
bool operator< (const StaticVector<T,N>& x, const StaticVector<T,N>& y) {
return std::lexicographical_compare(x.begin(),x.end(),y.begin(),y.end());
}
template<class T, std::size_t N>
bool operator!= (const StaticVector<T,N>& x, const StaticVector<T,N>& y) {
return !(x==y);
}
template<class T, std::size_t N>
bool operator> (const StaticVector<T,N>& x, const StaticVector<T,N>& y) {
return y<x;
}
template<class T, std::size_t N>
bool operator<= (const StaticVector<T,N>& x, const StaticVector<T,N>& y) {
return !(y<x);
}
template<class T, std::size_t N>
bool operator>= (const StaticVector<T,N>& x, const StaticVector<T,N>& y) {
return !(x<y);
}
// global swap()
template<class T, std::size_t N>
inline void swap (StaticVector<T,N>& x, StaticVector<T,N>& y) {
x.swap(y);
}
#if defined(__SUNPRO_CC)
// Trac ticket #4757; the Sun Solaris compiler can't handle
// syntax like 'T(&get_c_array(boost::StaticVector<T,N>& arg))[N]'
//
// We can't just use this for all compilers, because the
// borland compilers can't handle this form.
namespace detail {
template <typename T, std::size_t N> struct c_array
{
typedef T type[N];
};
}
// Specific for boost::StaticVector: simply returns its elems data member.
template <typename T, std::size_t N>
typename detail::c_array<T,N>::type& get_c_array(boost::StaticVector<T,N>& arg)
{
return arg.elems;
}
// Specific for boost::StaticVector: simply returns its elems data member.
template <typename T, std::size_t N>
typename const detail::c_array<T,N>::type& get_c_array(const boost::StaticVector<T,N>& arg)
{
return arg.elems;
}
#else
// Specific for boost::StaticVector: simply returns its elems data member.
template <typename T, std::size_t N>
T(&get_c_array(boost::StaticVector<T,N>& arg))[N]
{
return arg.elems;
}
// Const version.
template <typename T, std::size_t N>
const T(&get_c_array(const boost::StaticVector<T,N>& arg))[N]
{
return arg.elems;
}
#endif
#if 0
// Overload for std::array, assuming that std::array will have
// explicit conversion functions as discussed at the WG21 meeting
// in Summit, March 2009.
template <typename T, std::size_t N>
T(&get_c_array(std::array<T,N>& arg))[N]
{
return static_cast<T(&)[N]>(arg);
}
// Const version.
template <typename T, std::size_t N>
const T(&get_c_array(const std::array<T,N>& arg))[N]
{
return static_cast<T(&)[N]>(arg);
}
#endif
} /* namespace boost */
#if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
# pragma warning(pop)
#endif
#endif /*BOOST_STATIC_VECTOR_HPP*/
You broke the rule of three. You need a copy constructor. Note that your constructor taking const StaticVector<T,SizeRHS>& is not a copy constructor as it is a template. Hence when a StaticVector is copied (e.g. possibly when returned from a function) the compiler generated copy constructor is used, copying the storage (that's okay, it's a POD). At destruction time, the destructor accesses the storage as if it contained live objects, and that's terrible.
boost::aligned_storage is in fact a POD type (required by C++11), and has no knowledge of the type stored within. It just provides 'storage'. If you want to copy objects from one storage to another, you will have to perform the copy yourself.
Have you considered implementing your own allocator based on stack storage instead, and reuse the standard containers?