Why can't you put a const object into a STL container? - c++

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.

Related

transform vector of shared_ptr with non const elements to vector of shared_ptr with const elements

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.

C++ iterator to initialize collections of objects with no default constructor

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);
}

Square bracket [] operator overloading c++

I have a project that wants me to make a BigNum class in c++ (university project)
and it said to overload operator bracket for get and set
but the problem is if the set was invalid we should throw an exception the invalid is like
BigNum a;
a[i]=11;//it is invalid because its >9
in searching I found out how to make the set work
C++ : Overload bracket operators [] to get and set
but I didn't find out how to manage setting operation in c# you easily can manage the set value what is the equivalent of it in c++
to make it clear in C# we can say
public int this[int key]
{
set
{
if(value<0||value>9)throw new Exception();
SetValue(key,value);
}
}
New Answer
I have to rewrite my answer, my old answer is a disaster.
The check should happen during the assignment, when the right hand side (11) is available. So the operator which you need to overload is operator=. For overloading operator=, at least one of its operands must be an user defined type. In this case, the only choice is the left hand side.
The left hand side we have here is the expression a[i]. The type of this expression, a.k.a the return type of operator[], must be an user defined type, say BigNumberElement. Then we can declare an operator= for BigNumberElement and do the range check inside the body of operator=.
class BigNum {
public:
class BigNumberElement {
public:
BigNumberElement &operator=(int rhs) {
// TODO : range check
val_ = rhs;
return *this;
}
private:
int val_ = 0;
};
BigNumberElement &operator[](size_t index) {
return element_[index];
}
BigNumberElement element_[10];
};
OLD answer
You can define a wapper, say NumWapper, which wraps a reference of BigNum's element. The operator= of BigNum returns the wrapper by value.
a[i]=11;
is then something like NumWrapper x(...); x = 11. Now you can do those checks in the operator= of NumWrapper.
class BigNum {
public:
NumWrapper operator[](size_t index) {
return NumWrapper(array_[index]);
}
int operator[](size_t index) const {
return array_[index];
}
};
In the NumWrapper, overload some operators, such as:
class NumWrapper {
public:
NumWrapper(int &x) : ref_(x) {}
NumWrapper(const NumWrapper &other) : ref_(other.ref_) {}
NumWrapper &operator=(const NumWrapper &other);
int operator=(int x);
operator int();
private:
int &ref_;
};
You can also declare the NumWrapper's copy and move constructor as private, and make BigNum his friend, for preventing user code from copying your wrapper. Such code auto x = a[i] will not compile if you do so, while user code can still copy the wrapped value by auto x = static_cast<T>(a[i]) (kind of verbose though).
auto &x = a[i]; // not compiling
const auto &x = a[i]; // dangerous anyway, can't prevent.
Seems we are good.
These is also another approach: store the elements as a user defined class, say BigNumberElement. We now define the class BigNum as :
class BigNum {
// some code
private:
BigNumberElement array_[10];
}
We need to declare a whole set operators for BigNumberElement, such as comparison(can also be done through conversion), assignment, constructor etc. for making it easy to use.
auto x = a[i] will now get a copy of BigNumberElement, which is fine for most cases. Only assigning to it will sometimes throw an exception and introduce some run-time overhead. But we can still write auto x = static_cast<T>(a[i]) (still verbose though...). And as far as I can see, unexpected compile-time error messages is better than unexpected run-time exceptions.
We can also make BigNumberElement non-copyable/moveable... but then it would be the same as the first approach. (If any member functions returns BigNumberElement &, the unexpected run-time exceptions comes back.)
the following defines a type foo::setter which is returned from operator[] and overloads its operator= to assign a value, but throws if the value is not in the allowed range.
class foo
{
int data[10];
public:
void set(int index, int value)
{
if(value<0 || value>9)
throw std::runtime_error("foo::set(): value "+std::to_string(value)+" is not valid");
if(index<0 || index>9)
throw std::runtime_error("foo::set(): index "+std::to_string(index)+" is not valid");
data[index] = value;
}
struct setter {
foo &obj;
size_t index;
setter&operator=(int value)
{
obj.set(index,value);
return*this;
}
setter(foo&o, int i)
: obj(o), index(i) {}
};
int operator[](int index) const // getter
{ return data[index]; }
setter operator[](int index) // setter
{ return {*this,index}; }
};
If what you are trying to do is overload [] where you can input info like a dict or map like dict[key] = val. The answer is actually pretty simple:
lets say you want to load a std::string as the key, and std::vector as the value.
and lets say you have an unordered_map as your underlying structure that you're trying to pass info to
std::unordered_map<std::string, std::vector<double>> myMap;
Inside your own class, you have this definition:
class MyClass{
private:
std::unordered_map<std::string, std::vector<double>> myMap;
public:
std::vector<double>& operator [] (std::string key) {
return myMap[key];
}
}
Now, when you want to load your object, you can simply do this:
int main() {
std::vector<double> x;
x.push_back(10.0);
x.push_back(20.0);
x.push_back(30.0);
x.push_back(40.0);
MyClass myClass;
myClass["hello world"] = x;
double x = myClass["hello world"][0]; //returns 10.0
}
The overloaded [] returns a reference to where that vector is stored. So, when you call it the first time, it returns the address of where your vector will be stored after assigning it with = x. The second call returns the same address, now returning the vector you had input.

An array in a class without initialization

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.

Container of fixed dynamic size

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);
}
};