Vector and []-operator overloading - c++

I have inherited my class from std::vector. Now I want to overload the []-operator.
When I try to assign a new value to my vector, e.g. v[0]=5, I should receive the message OK.
This is my code (I know, that makes no sense, I'm just playing around):
#include<vector>
#include<iostream>
class Vec : public std::vector<int> {
public:
int operator[](int);
};
int Vec::operator[](int i) {
(*this)[i] = i;
std::cout << "OK";
return 123;
}
int main() {
Vec v;
v[0]=5;
}
Unfortunately I get the following error:
In member function ‘int Vec::operator[](int)’:
error: lvalue required as left operand of assignmen
In function ‘int main()’:
error: lvalue required as left operand of assignment

This particular error is caused because you are not returning an lvalue, generally defined as something that can appear to the left of an assignment, such as v[0] = 5;. You have more problems as pointed out in the other answers but this is the specific issue you face with that error message (a).
The correct specification for overloading the index operator is:
int& operator[] (const int nIndex);
You have to return a reference to the item (so it can be modified) if you want to treat it as an lvalue. The following code shows a fix, although obviously all array indexes map to the same value in this simplified case:
#include <vector>
#include <iostream>
class Vec : public std::vector<int> {
public:
int& operator[] (int); // <-- note the '&'
private:
int xyzzy;
};
int& Vec::operator[] (int idx) { // <-- note the '&'
std::cout << "OK\n";
return xyzzy;
}
int main () {
Vec v;
v[0] = 5;
v[1] = 6;
std::cout << v[22] << '\n';
return 0;
}
The output of this is:
OK
OK
OK
6
In reality, you wouldn't map all indexes to the same value, the code above is simply to illustrate the correct function signature. I haven't bothered to give a more complete example since subclassing classes with non-virtual destructors regularly leads to problems in non-trivial code (b).
(a) It's not usually considered a good idea to subclass std::vector since the destructor isn't virtual, so you can get into trouble when trying to destroy an object polymorphically.
You're probably better off using a has-a relationship (where your class contains a vector) rather than an is-a relationship (where you inherit).
That unfortunately means you may have to create a lot of pass-through methods from your class to the underlying vector (although only the ones you need) but it will solve the problem with the destructor.
(b) See (a) :-)

You'd need to return a reference to your element - however note that even if you did, you'd run into inifinite recursion - your operator[] calls itself.
Either way - inheriting from std::vector isn't a good idea. Use composition instead.

The code below illustrates how to call the operator[] from the vector base class....
#include <iostream>
#include <vector>
struct Vec : std::vector<int>
{
int& operator[](int n)
{
std::cout << "operator[](" << n << ")\n";
return std::vector<int>::operator[](n);
}
};
int main()
{
Vec v;
v.push_back(10);
v.push_back(20);
v[0] += 5;
std::cout << v[0] << ' ' << v[1] << '\n';
}
Output when I run it:
operator[](0)
operator[](1)
operator[](0)
15 20
Don't take all this talk about "do not inherit from std::vector" too seriously: you have to go out of your way to delete a dynamically allocated Vec using a std::vector<int>*, or do an accidental by-value slicing copy - and even then it'd probably only bite you if you had added data members. You should make sure you understand those risks then make your own assessment, but for small utility programs etc. it's productive to inherit from such classes sometimes....

Related

Why this code does allow to push_back unique_ptr do vector?

so I thought adding unique to vector shouldn't work.
Why does it work for the below code?
Is it cause by not setting copy ctor as "deleted"??
#include <iostream>
#include <vector>
#include <memory>
class Test
{
public:
int i = 5;
};
int main()
{
std::vector<std::unique_ptr<Test>> tests;
tests.push_back(std::make_unique<Test>());
for (auto &test : tests)
{
std::cout << test->i << std::endl;
}
for (auto &test : tests)
{
std::cout << test->i << std::endl;
}
}
There is no copy here, only moves.
In this context, make_unique will produce an instance of unique pointer which is not named, and this push_back sees it as a r-value reference, which it can use as it wants.
It produce pretty much the same result than this code would:
std::vector<std::unique_ptr<Test>> tests;
auto ptr = std::make_unique<Test>();
tests.push_back(std::move(ptr));
This is called move semantics if you want to search more info on the matter. (and this only works from c++11 and beyond)
There are two overloads of std::vector::push_back according to https://en.cppreference.com/w/cpp/container/vector/push_back
In your case you will use the one with rvalue-ref so no copying required.

How to create a vector with an initial size by using a custom constructor and without calling the destructor? [duplicate]

This question already has answers here:
What is The Rule of Three?
(8 answers)
Closed 4 years ago.
Take the following example:
#include <iostream>
#include <stdio.h>
#include <vector>
using namespace std;
class Foo{
public:
Foo(){
cout << "Constructing the default way..." << endl;
this->num = (int*)malloc(sizeof(int));
}
Foo(int a){
cout << "Using a custom constructor..." << endl;
this->num = (int*)malloc(sizeof(int) * a);
}
~Foo(){
cout << "Destructing..." << endl;
free((void*)num);
}
int* num;
};
int main()
{
vector<Foo> objects(5);
for(Foo& v : objects) printf("%x0x\n", v.num);
cout << "\n---------------\n";
cout << "END OF PROGRAM!\n";
cout << "---------------\n\n";
return 0;
}
Creating the vector by passing it an initial count of objects creates every single object individually, hence, all of them have their num at different addresses and their destructors are called at the end of the program.
However, if I want to create the vector by giving it a custom constructor like vector<Foo> objects(5, Foo(5)); (This is only my guess) a temporary object is constructed and then copied to every object in the array. Effectively, this makes every single object in the vector have their num pointer point to the same memory. Plus that memory gets freed when the initialization is finished and then every single object becomes useless.
How do I go around this without making the custom constructor into a new Init(int) function and running it on every single object in the vector? (Of course changing malloc into realloc).
You could solve your problem with a vector of unique_ptr's to the Foo objects.
class Foo
{
public:
Foo(int i) {}
// ...
};
#include <algorithm>
#include <vector>
auto main() -> int
{
std::vector<std::unique_ptr<Foo>> foos(100);
std::generate(foos.begin(), foos.end(), []() { return std::make_unique<Foo>(5); });
}
However, you don't want to do this because otherwise you would have to go the extra indirection with the unique_ptr when calling anything on a vector's Foo object.
As others have already suggested, you should store Foo's int members in a vector. Then you can just reserve the space in the vector and emplace_back (construct) your Foo objects directly in the vector of Foo's.
#include <vector>
class Foo
{
public:
Foo() {}
Foo(int i) : ints(i) {}
private:
std::vector<int> ints;
};
namespace
{
constexpr auto NumberOfFoos = 100;
}
auto main() -> int
{
std::vector<Foo> foos;
foos.reserve(NumberOfFoos);
for (auto i = 0; i < NumberOfFoos; ++i) {
foos.emplace_back(10);
}
}
Regarding your code, you might want to have a look at these additional sources and answers (as already suggested by other comments):
Rule of three
Stop using std::endl
Why is “using namespace std” considered bad practice?
Two problems preventing a solution.
1) You need to implement your custom copy and assignment operators to do the deep copying since the default shallow copy (as you point out) doesn't do the right thing.
2) You need to let the object remember how large an array you are allocating for the copy/assignment implementations to work.

Vector of non-const objects seems to be treated as constant in range-based for loop

I have a std::vector of objects being filled by de-referencing std::unique_ptr's in the push_back calls. However, when I run through a mutable range-based for-loop, my modification to these objects stays local to the loop. In other words, it seems as those objects are being treated as constant, despite that lack of a const keyword in the loop. Here is minimal code to demonstrate what I'm seeing:
#include <vector>
#include <memory>
#include <iostream>
class Item
{
public:
typedef std::unique_ptr<Item> unique_ptr;
inline static Item::unique_ptr createItem()
{
return std::unique_ptr<Item>(new Item());
}
inline const int getValue() const { return _value; }
inline void setValue(const int val) { _value = val; }
private:
int _value;
};
int main()
{
std::vector<Item> _my_vec;
for (int i = 0; i < 5; i++)
{
Item::unique_ptr item = Item::createItem();
_my_vec.push_back(*item);
}
for (auto item : _my_vec)
{
// modify item (default value was 0)
item.setValue(10);
// Correctly prints 10
std::cout << item.getValue() << std::endl;
}
for (auto item : _my_vec)
{
// Incorrectly prints 0's (default value)
std::cout << item.getValue() << std::endl;
}
}
I suspect this has something to do with the move semantics of std::unique_ptr? But that wouldn't quite make sense because even if push_back is calling the copy constructor or something and copying the added item rather than pointing to it, the iterator is still passing over the same copies, no?
Interestingly enough, in my actual code, the class represented here by Item has a member variable that is a vector of shared pointers to objects of another class, and modifications to the objects being pointed to by those shared pointers persist between loops. This is why I suspect there's something funky with the unique_ptr.
Can anyone explain this behavior and explain how I may fix this issue while still using pointers?
When you write a range-based for loop like that:
std::vector<int> v = ...;
for(auto elt : v) {
...
}
the elements of v are copied into elt.
In your example, in each iteration, you modify the local copy of the Item and not the Item in the vector.
To fix your issue, use a reference:
for (auto& item : _my_vec)
{
item.setValue(10);
std::cout << item.getValue() << std::endl;
}
Vector of non-const objects seems to be treated as constant
If it was treated as constant, then the compiler would scream at you, because writing to a constant is treated as ill-formed and the compiler would be required to scream at you. The shown code compiles just fine, with no warnings.
I suspect that you may be referring to the fact that you don't modify the elements within the vector. That is because you modify auto item. That item is not an element of the vector, it is a copy of the item in the vector. You could refer to the item within that vector by using a reference: auto& item. Then modifications to item would be modifications to the referred element of the vector.

reference-to-cont class member initialized with non-const value

I have a class that should, as its input data, either use a reference to external data (without copying), or create the data itself based on other input. I prefer using references (to avoid dereferencing, since the data are matrices) and ended up with the following structure (simplified):
#include <iostream>
#include <vector>
using namespace std;
using VectI = vector<int>;
class A {
VectI x0;
VectI const & x = x0;
public:
A(VectI const & extX) : x(extX) {} // referencing existing external data
A(int i) { x0.push_back(i); x0.push_back(i*i); } // create from other data
void show_x()
{ cout << "x ="; for (auto&& i : x) cout << " " << i; cout << endl; }
};
int main() {
VectI myX = {1, 2, 3};
A a(myX); a.show_x(); // a references myX
A b(2); b.show_x(); // b creates its own data based on i=2
return 0;
}
The example works:
x = 1 2 3
x = 2 4
but are there any potential problems with this approach?
In particular, is the fact that I am changing x0 that is referenced by the const vector x 'legal' C++, or is it something that other compilers might complain about?
Also, can I be sure that the first constructor avoids copying data?
This is fine in the C++11 standard but your code is very brittle.
In particular the compiler generated copy and move constructors and the assignment operator will not work correctly, so you'll have to build your own.
You might also encounter dangling references: remember that the object lifetime extension due to a const reference is not transitive. The behaviour on using A(VectI const & extX) with an anonymous temporary is undefined.
Using a pointer to VectI - perhaps even a std::unique_ptr to a VectI along with a concept of ownership might be a safer way to go.

Safety of map operator[] when you dont want a default constructor

I recently ran into an interesting bug with a program at work. I would like to know a little bit more about how operator[] works with maps. Consider the following example code:
#include <map>
#include <iostream>
#include <utility>
#include <tuple>
class test
{
public:
test(int a, char b) {
a_ = a;
b_ = b;
}
void print() {
std::cout << a_ << " " << b_ << std::endl;
}
private:
int a_;
char b_;
};
int main()
{
std::map<int, test> mapper;
mapper.emplace(std::piecewise_construct,
std::forward_as_tuple<int>(1),
std::forward_as_tuple<int, char, double>(1, 'c', 2.34));
mapper[1].print();
}
The code is very simple, and the objective is pretty clear, I dont want to have a default constructor for the class test. This code wont compile however, because mapper[1].print() calls the default constructor.
The obvious solution here is to replace mapper[1].print() with mapper.find(1)->second.print(). I am just interested in why it is necessary for mapper[1] to create a test. Does this mean that every time in any code i have written with a map using operator[] that it creates a default and copies the class before calling its functions?
This seems like a huge waste of time, i understand its necessity during something like this: mapper[2] = test(1, 'a'). Is there not any way that it could tell that its not part of an operator= call? I guess not.
from Visual Studio 2012 library
mapped_type& operator[](key_type&& _Keyval)
{ // find element matching _Keyval or insert with default mapped
iterator _Where = this->lower_bound(_Keyval);
if (_Where == this->end()
|| this->_Getcomp()(_Keyval, this->_Key(_Where._Mynode())))
_Where = this->insert(_Where,
pair<key_type, mapped_type>(
_STD move(_Keyval),
mapped_type())); // <---- call to default constructor
return (_Where->second);
}
as you can see it require mapped_type to be default construable to compile doesn't means it need to construct the object every time you call operator[]