Is there a standard container for a sequence of fixed length, where that length is determined at runtime. Preferrably, I'd like to pass an argument to the constructor of each sequence element, and use that argument to initialize a const member (or a reference). I'd also like to obtain the sequence element at a given index in O(1). It seems to me that all of my requirements cannot be met at the same time.
I know std::array has fixed length, but that length has to be known at compile-time.
std::vector has dynamic size, and allows passing contructor arguments using emplace. Although you can reserve memory to avoid actual reallocations, the type still has to be movable to theoretically allow such reallocations, which e.g. prevents const members.
Then there is std::list and std::forward_list, which don't require a movable type, but which are still resizable and will perform rather poorly under random-access patterns. I also feel that there might be considerable overhead associated with such lists, since each list node will likely be allocated separately.
Strangely enough, std::valarray is my best bet so far, since it has a fixed length and won't resize automatically. Although there is a resize method, your type won't have to be movable unless you actually call that method. The main deficit here is the lack for custom constructor arguments, so initializing const members isn't possible with this approach.
Is there some alternative I missed? Is there some way to adjust one of the standard containers in such a way that it satisfies all of my requirements?
Edit: To give you a more precise idea of what I'm trying to do, see this example:
class A {
void foo(unsigned n);
};
class B {
private:
A* const a;
const unsigned i;
public:
B(A* aa) : a(aa), i(0) { }
B(A* aa, unsigned ii) : a(aa), i(ii) { }
B(const std::pair<A*, unsigned>& args) : B(args.first, args.second) { }
B(const B&) = delete;
B(B&&) = delete;
B& operator=(const B&) = delete;
B& operator=(B&&) = delete;
};
void A::foo(unsigned n) {
// Solution using forward_list should be guaranteed to work
std::forward_list<B> bs_list;
for (unsigned i = n; i != 0; --i)
bs_list.emplace_front(std::make_pair(this, i - 1));
// Solution by Arne Mertz with single ctor argumen
const std::vector<A*> ctor_args1(n, this);
const std::vector<B> bs_vector(ctor_args1.begin(), ctor_args1.end());
// Solution by Arne Mertz using intermediate creator objects
std::vector<std::pair<A*, unsigned>> ctor_args2;
ctor_args2.reserve(n);
for (unsigned i = 0; i != n; ++i)
ctor_args2.push_back(std::make_pair(this, i));
const std::vector<B> bs_vector2(ctor_args2.begin(), ctor_args2.end());
}
Theoretically vector has the properties you need. As you noted, actions that possibly do assignments to the contained type, including especially any sequence modifications (empace_back, push_back, insert etc.) are not supported if the elements are noncopyable and/or nonassignable. So to create a vector of noncopyable elements, you'd have to construct each element during vector construction.
As Steve Jessop points out in his answer, if you define the vector const in the first place you won't even be able to call such modifying actions - and of course the elements remain unchanged as well.
If I understand correctly, you have only a sequence of constructor arguments, not the real object sequence. If it's only one argument and the contained type has a corresponding constructor, things shoule be easy:
struct C
{
const int i_;
C(int i) : i_(i) {}
};
int main()
{
const std::vector<C> theVector { 1, 2, 3, 42 };
}
If the constructor is explicit, you have to make a list first or explicitly construct the objects in the initializer-list:
int main()
{
auto list = { 1, 2, 3, 4 };
const std::vector<C> theVector (std::begin(list), std::end(list));
const std::vector<C> anotherVector { C(1), C(44) };
}
If it's more than just one argument per constructed object, consider a intermediate creator object:
struct C
{
const int i_;
C(int i, int y) : i_(i+y) {}
};
struct CCreator
{
int i; int y;
explicit operator C() { return C(i,y); }
};
int main()
{
const std::vector<CCreator> ctorArgs = { {1,2}, {3,42} };
const std::vector<C> theVector { begin(ctorArgs), end(ctorArgs) };
}
I think const std::vector<T> has the properties you ask for. Its elements aren't actually defined with const, but it provides a const view of them. You can't change the size. You can't call any of the member functions that need T to be movable, so for normal use they won't be instantiated (they would be if you did an extern class declaration, so you can't do that).
If I'm wrong, and you do have trouble because T isn't movable, try a const std::deque<T> instead.
The difficulty is constructing the blighter -- in C++11 you can do this with an initializer list, or in C++03 you can construct a const vector from a non-const vector or from anything else you can get iterators for. This doesn't necessarily mean T needs to be copyable, but there does need to be a type from which it can be constructed (perhaps one you invent for the purpose) .
Add a level of indirection by using a std::shared_ptr. The shared pointer can be copied and assigned as usual, but without modifying the object that is pointed to. This way you should not have any problems, as the following example shows:
class a
{
public:
a(int b) : b(b) { }
// delete assignment operator
a& operator=(a const&) = delete;
private:
// const member
const int b;
};
// main
std::vector<std::shared_ptr<a>> container;
container.reserve(10);
container.push_back(std::make_shared<a>(0));
container.push_back(std::make_shared<a>(1));
container.push_back(std::make_shared<a>(2));
container.push_back(std::make_shared<a>(3));
Another advantage is the function std::make_shared which allows you to create your objects with an arbitrary number of arguments.
Edit:
As remarked by MvG, one can also use std::unique_ptr. Using boost::indirect_iterator the indirection can be removed by copying the elements into a new vector:
void A::foo(unsigned n)
{
std::vector<std::unique_ptr<B>> bs_vector;
bs_vector.reserve(n);
for (unsigned i = 0; i != n; ++i)
{
bs_vector.push_back(std::unique_ptr<B>(new B(this, i)));
}
typedef boost::indirect_iterator<std::vector<std::unique_ptr<B>>::iterator> it;
// needs copy ctor for B
const std::vector<B> bs_vector2(it(bs_vector.begin()), it(bs_vector.end()));
// work with bs_vector2
}
I also encounter this problem, the use case in my code is to provide a thread-safe vector, the elements number is fixed and are atomic numbers. I have read all the great answers here. I think we may also consider my solution:
Just inherited the std::vector and hide the modifiers such as push_back, emplace_back, erase, then we get a fixed size vector. We can only access and modify the elements with operator [].
template <typename T>
class FixedVector : protected std::vector<T> {
public:
using BaseType = std::vector<T>;
FixedVector(size_t n) : BaseType(n) {}
FixedVector(const T &val, size_t n) : BaseType(val, n) {}
typename BaseType::reference operator[](size_t n) {
return BaseType::operator[](n);
}
};
Related
I have a class A containing a vector of shared_ptr<B>.
I implemented a getter to this vector.
In some cases, it would be nice to ensure that the content in B does not change (make B read only or a const reference).
If I would not have used vector<shared_ptr<B>> but rather vector<B> I could simply write two getters, one returning a const reference (read only), and one returning a reference only (manipulation possible). #
Is there a way to do the same thing with a vector<shared_ptr<B>>?
Maybe it is easier to understand the problem in this code:
#include <vector>
#include <memory>
using namespace std;
class B{
public:
explicit B(int i) : i_{i} {}
void set_i(int i){i_ = i;}
private:
int i_ = 0;
};
class A{
public:
const vector<shared_ptr<B>> &get_vb(){return vb;}
// const vector<shared_ptr<const B>> &get_vb_const(){return vb;} // I would like to return a const vector with const elements in some cases
private:
vector<shared_ptr<B>> vb{make_shared<B>(1), make_shared<B>(10), make_shared<B>(100)};
};
int main() {
A a;
const auto &vb = a.get_vb();
vb[0]->set_i(2);
// const auto &vb_const = a.get_vb_const(); // somehow I would like to gain this vector without being able to modify the elements
// vb_const[0]->set_i(2); // should throw error
return 0;
}
You need to construct a new vector with the desired elements:
const vector<shared_ptr<const B>> get_vb_const() const {
return vector<shared_ptr<const B> > {vb.cbegin(), vb.cend()};
}
Note that the function doesn't return a reference now because we are creating a temporary and returning it.
transform vector of shared_ptr with non const elements to vector of shared_ptr with const elements
You can use the constructor of vector that accepts a pair of iterators to perform the conversion.
You can avoid the overhead of allocating and copying a vector by implementing a custom const iterator for your class.
I have a class Foo with no sensible default constructor. I would also prefer to keep the copy-assignment operator private, although that may become impossible. (I'd like to make the class “almost” immutable, whence thread-safe, by having const fields and the small number of mutators that cast const way as private and early in the object lifetime.)
Creating std::vector<Foo> under these constraints is a little bit of a challenge. I came up with a solution I haven't seen elsewhere (see, for example, earlier SO question 1). I have a custom iterator which, when dereferenced, creates a Foo. It is set up such that each invocation increments to the next value of Foo in the vector. The sequence is easy to define. I define operator++, next, advance, distance and operator* on CustomIterator.
Then I have
std::vector<Foo> foo_vec{CustomIterator(0), CustomIterator(size_of_vector)};
No access issues. No unnecessary constructions. No copies. Anyone see a problem with this?
I will summarize the comments. The simple factory generates vector of initialized elements.
#include <vector>
class X {
explicit X(int value) : value_(value) {}
X& operator=(const X&) = default;
friend std::vector<X> generate(int from, int to);
public:
const int value_;
};
// simplest factory ever
std::vector<X> generate(int from, int to) {
std::vector<X> result;
result.reserve(to - from);
for (int k = from; k < to; ++k) {
result.emplace_back(std::move(X(k)));
}
return std::vector<X>();
}
int main() {
auto v = generate(0, 10);
static_cast<void>(v);
}
I have a slight problem. I have a Matrix class defined as follows (in row-major form):
template<typename T>
class Matrix {
private:
class RowVector {
private:
T *_vec;
std::size_t _l;
public:
RowVector(T *vec, std::size_t l);
const T &operator[](std::size_t index) const;
T &operator[](std::size_t index);
operator std::vector<T>() const;
};
std::vector<T> _data;
std::size_t _m;
std::size_t _n;
public:
Matrix(std::size_t m, size_t n, const T &elem = T());
const RowVector operator[](std::size_t index) const;
RowVector operator[](std::size_t index);
std::size_t getm() const;
std::size_t getn() const;
void fill(const T &elem);
void fillRow(std::size_t index, const T &elem);
void fillCol(std::size_t index, const T &elem);
Matrix &transpose(unsigned int i = 1);
const std::vector<T> &data() const;
};
and wish to overload two RowVector operators=
typename Matrix<T>::RowVector &operator=(const std::vector<T> &vec);
typename Matrix<T>::RowVector &operator=(const Matrix<T> &mat);
so I can return a RowVector & using A[0] and reassign its value using either a vector or a matrix. Keep in mind that I (presumably) can ignore the rule of three because I provide no explicit way for the client to construct a RowVector object.
However, in attempting to write the function bodies for the overloads, I have come across a problem: that
(1) I cannot copy-construct a vector/Matrix object that will persist outside of the operator='s scope so that I can assign its data() to _vec and its size() to _l.
(2) I cannot directly modify _data since it is not a static variable; even if I could, I have no way to discover the index so I can overwrite the relevant region of memory in the enclosing Matrix object.
Do you know of any way in which this can be done? These would be two very useful assets for my class.
I would like to be able to write something like this:
Matrix<int> A(3, 4);
std::vector<int> v {1, 2, 3, 4};
Matrix<int> row(1, 4, 3);
// *****************
A[0] = v;
A[1] = row;
// *****************
(Hopefully my variable names are self-explanatory)
I think my prototypes are correct, but I just can't find a way to do this.
Thanks!
T *_vec;
std::size_t _l;
This is a problematic design! I don't say it is incorrect per se, but you then need to correctly manage the memory yourself. Ignoring the rule of three (five) is a very dangerous in this respect. You have a pointer to (potentially?) dynamically allocated memory, so there must be some instance that is responsible for its deletion (must not necessarily be your RowVector, but what else then?).
From pure technical aspect, you even can let _vec point to some vector's data, provided you gurantee that this other vector lives as long as you want to access the data via pointer – which in general requires, though, quite some effort.
Safest would be having each row maintain its own data, copying (or moving) it from the other vector. Then easiest is storing the data in a std::vector of its own (replacing the raw pointer).
If you want to avoid copying data around and instead share data between different matrices and their rows, then you could maintain the data via a std::shared_ptr - either maintaining a raw array or possibly even a heap-allocated std::vector.
If you opt for either std::vector or std::shared_ptr, then copy and move constructors and assignment operators get totally simple:
class C
{
public:
C(C const&) = default;
C(C&&) = default;
C& operator= (C const&) = default;
C& operator= (C&&) = default;
};
All of these defaults will do copying/moving as per member, and both std::vector and std::shared_ptr have the appropriate constructors and operators available already, so you'd be fine – and you now can violate the rule of five, dropping the destructor, as the default one (calling all the member's destructors) suffices.
If you consider shared pointers: be aware that you then cannot assign a std::vector's data to: std::vector does its own memory management, and you will end up in double deletion of, so in this specific case, you'd still have to create a copy of. You might possibly end up in multiple constructors and assignment operators:
std::shared_ptr<std::vector<int>> _data;
// assign shared pointers
RowVector(RowVector const&) = default;
RowVector(RowVector&&) = default;
// need to create copies of: we never know about the scope of the vector passed!
RowVector(std::vector<int> const& data) : _data(new std::vector<int>(data)) { }
RowVector(std::vector<int>&& data) : _data(new std::vector<int>(std::move(data))) { }
// we *are* sharing already -> no need to copy:
RowVector(std::shared_ptr<std::vector<int>& data) : _data(data) { }
Assignment operators analogously.
Side-note: If you want to have a mathmatical nxm matrix, pretty sure you don't want to have a jagged array. I'd assume your Matrix class' constructor already creates an appropriate vector of vectors, then for assignment, you'd have yet to check length:
// defaults not suitable any more!
RowVector& RowVector::operator=(RowVector const& other)
{
// still assuming shared pointer:
// (for vector, replace -> with .)
if(other._data->size() != _data.size())
{
throw SomeException();
}
_data = other._data;
}
RowVector(RowVector&& other)
{
if(other._data->size() != _data.size())
{
throw SomeException();
}
_data = std::move(other._data);
}
I have been struggling with that issue long time. It seems to be quite ineffective in C++ that array of class B created in class A have to be initialized by default constructor. Is there any way to avoid that behavior ? I implement a register of people. If I create it with count references, I get a lot of default constructor callings and it seems to be less effective that it should be. Also I have to create default constructor which is unnecessary.
When you are creating an array of objects, be it a static array (Person people[1000]) or dynamically allocated (Person* people = new Person[1000]), all 1000 objects will be created and initialized with the default constructor.
If you want to create space for the objects, but not create them just yet, you can either use a container like std::vector (which implements a dynamically sized array), or use an array of pointers, like Person* people[1000] or Person** people = new Person*[1000] - in this case, you can initialize all items with NULL to indicate empty records, and then allocate objects one by one: people[i] = new Person(/* constructor arguments here */), but you will also have to remember to delete each object individually.
I think I have the solution you are aiming for. I tested this on GCC 4.6 and it may require modification for MSVC++ for the alignment bit, but here is the sample output and the source code:
Source Code (tested with GCC 4.6):
#include <cstdio>
#include <cstring>
#include <new>
// std::alignment_of
template <typename T, std::size_t capacity>
class StaticVector
{
public:
StaticVector() : _size(0)
{
// at this point we've avoided actually initializing
// the unused capacity of the "static vector"
}
~StaticVector()
{
// deconstruct in reverse order of their addition
while (!empty())
pop_back();
}
void push_back(const T &src)
{
// at this point we initialize the unused array entry by copy
// constructing it off the passed value
new (data() + _size) T(src);
_size++;
}
void pop_back()
{
_size--;
// we manually call the deconstructor of the entry we are marking as unused
data()[_size].~T();
}
bool empty() const {return _size == 0;}
std::size_t size() const {return _size;}
// NOTE: you'd better index only into constructed data! just like an std::vector
T & operator[](int i) {return *data()[i];}
const T & operator[](int i) const {return *data()[i];}
T * data() {return reinterpret_cast<T*>(_data);}
const T * data() const {return reinterpret_cast<const T*>(_data);}
protected:
// NOTE: I only tested this on GCC 4.6, it will require some
// conditional compilation to work with MSVC and C++11
#if 1 // for GCC without c++11
char _data[sizeof(T[capacity])] __attribute__((aligned(__alignof__(T))));
#else // UNTESTED: The C++11 way of doing it?
alignas(T) char _data[sizeof(T[capacity])]; // create a suitable sized/aligned spot for the array
#endif
std::size_t _size;
};
// NOTE: lacks a default constructor, only
// constuctor that takes parameters
class B
{
public:
B(int param1, const char param2[])
{
printf("Constructing B at %08X with parameters (%i, %s)\n", (int)this, param1, param2);
x = param1;
strcpy(buffer, param2);
}
~B()
{
printf("Deconstructing B at %08X\n", (int)this);
}
// NOTE: only provided to do the printf's, the default
// copy constructor works just fine
B(const B &src)
{
printf("Copying B from %08X to %08X\n", (int)(&src), (int)this);
x = src.x;
memcpy(buffer, src.buffer, sizeof(buffer));
}
protected:
int x;
char buffer[128];
};
class A
{
public:
StaticVector<B, 8> staticVectorOfB;
};
int main()
{
printf("PROGRAM START\n");
A a;
a.staticVectorOfB.push_back(B(0, "Uno"));
a.staticVectorOfB.push_back(B(1, "Dos"));
a.staticVectorOfB.push_back(B(2, "Tres"));
printf("PROGRAM END\n");
return 0;
}
Sample Output:
PROGRAM START
Constructing B at 0022FDC4 with parameters (0, Uno)
Copying B from 0022FDC4 to 0022F9A0
Deconstructing B at 0022FDC4
Constructing B at 0022FE48 with parameters (1, Dos)
Copying B from 0022FE48 to 0022FA24
Deconstructing B at 0022FE48
Constructing B at 0022FECC with parameters (2, Tres)
Copying B from 0022FECC to 0022FAA8
Deconstructing B at 0022FECC
PROGRAM END
Deconstructing B at 0022FAA8
Deconstructing B at 0022FA24
Deconstructing B at 0022F9A0
First, you do not need to create default constructor, because otherwise the compiler will generate its code. I do not think there is a clean way to avoid calling default constructor on the object (perhaps optimizer would strip it out for the array), but there is surely a dirty one:
class B
{
};
class A
{
private:
char _array[sizeof(B)*5];
B* getB() {return (B*)_array;}
};
Then you can still use the pointer the same way as you would use fixed size array. sizeof and increment/decrement will not function though.
I guess you should not be bothered too much by "inefficiences" from default constructor. They are there for a reason. Otherwise, if default constructor really has no job to do, it should be inlined and then it will generate no overhead to execution.
How is the array, is class B inside A? Is it like B arr[size];? Instead use vector so that you can init the size in initialization and then push objects. Or dynamic array with new like below. The initfunc can create you register. Since the initfunc is called in initialization of the constructor it will be efficient.
class B
{
};
class A
{
B *barray;
B* initfunc()
{
B* tmp = new B[5];
//init elements of B
return tmp;
}
public:
A():barray(initfunc())
{
}
~A()
{
delete[] barray;
}
};
//the code is not exception safe, vector recommended.
See the code below - I am trying to put a const object into a vector. I know the answer is "STL containers require objects to be assignable and copy constructable", but, without citing the standard, can anyone explain what the problem with doing this is? I don't understand why a class like this could not be copied (besides that c++ doesn't allow it).
All it is is a value stored that is not allowed to be changed - why can't putting it in a vector simply create another one of these objects?
#include <vector>
// Attempt 1
// /home/doriad/Test/Test.cxx:3:8: error: non-static const member ‘const int MyClass::x’, can’t use default assignment operator
// struct MyClass
// {
// int const x;
// MyClass(int x): x(x) {}
// };
//
// int main()
// {
// std::vector<MyClass> vec;
// vec.push_back(MyClass(3));
// return 0;
// }
// Attempt 2
// /home/doriad/Test/Test.cxx:28:23: error: assignment of read-only member ‘MyClass::x’
struct MyClass
{
int const x;
MyClass(int x): x(x) {}
MyClass& operator= (const MyClass& other)
{
if (this != &other)
{
this->x = other.x;
}
return *this;
}
};
int main()
{
std::vector<MyClass> vec;
vec.push_back(MyClass(3));
return 0;
}
EDIT:
It is possible to do this with std::set and std::list. I guess it is the sort() function in std::vector that uses assignment. This is not UB right?
#include <set>
// Attempt 1
struct MyClass
{
int const x;
MyClass(int x): x(x) {}
bool operator< (const MyClass &other) const;
};
bool MyClass::operator<(const MyClass &other) const
{
if(this->x < other.x)
{
return true;
}
else if (other.x < this->x)
{
return false;
}
}
int main()
{
std::set<MyClass> container;
container.insert(MyClass(3));
return 0;
}
EDIT2: (Removing a bunch of stuff that doesn't have to work) The C++11 standard states that the insert method for vector and deque (and the default implementation of push_back for that matter) requires the value type to be CopyAssignable, i.e., the value supports:
t= v;
Classes and structs with const members are not CopyAssignable by default, so what you want to do won't work.
This doc (n3173) has an explanation for the various container requirements.
One possible solution would be to store pointers to the objects in the vector, because pointers are assignable and copy constructable.
Another possible solution would be to declare x without the const keyword, but ensure that it cannot be modified through encapsulation (i.e. you should declare it as private and don't modify from anywhere outside the constructor)..
When you place an object of type MyClass in the std::vector, the vector will make a copy of the object for storage, and not the object you passed to it.