I have private variable std::vector<some struct> Can i somehow pass the vector to function return value, but with no "write" access, so you will not be able to add new elements into it, and you won't be able to modify the elements(e.g. const std::vector<some const struct>*. How can i do that? The only idea i have is to create a new vector with constant pointers to existing elements. Is there a better solution?
You can't modify the elements of a vector via a pointer or reference to const vector. So the following would be safe:
const std::vector<some_type>* get_pstuff() const { return &the_vector; }
const std::vector<some_type>& get_rstuff() const { return the_vector; }
On the other hand, it might be more idiomatic to return const_iterators to the begin and end of the vector:
std::vector<some_type>::const_iterator cbegin() const { return the_vector.cbegin(); }
std::vector<some_type>::const_iterator cend() const { return the_vector.cend(); }
Related
I have a class that wraps a big array of bytes that are network packets. The class implements a queue and provides (among others) front() function that returns a const vector of bytes that constitute the oldest packet in the queue.
class Buffer{
unsigned char data[65536];
unsigned int offset;
unsigned int length;
[...]//other fields for maintaining write ptr etc.
public:
const std::vector<unsigned char> front(){
return std::vector<unsigned char>(data + offset, data + offset + length);
}
//other methods for accessing the queue like
//pop(), push(), clean() and so forth...
[...]
}
The performance of above implementation of front() function suffers from unnecessary copying bytes from the range occupied by the current packet. Since the vector is const, there is no need of making a copy of the data. What I want is to create a vector on the data that are already stored in the buffer. Of course destructor of the vector should not deallocate the memory.
You have some options available to you:
Rather than returning a vector, just return a const char*:
const char* front() {
return data;
}
Consider using a standard container, such as a string data as your Buffer member. This will allow you to do:
const string& front() {
return data;
}
The best option though is if you have C++17 or access to experimental::string_view you could just do:
const string_view front() {
return string_view(data);
}
Just a convention comment, there is going to be an expectation of front that it will behave like other standard containers, which:
Returns a reference to the first element in the container.
Calling front on an empty container is undefined.
[source]
Bringing front to apply to bare on fixed size arrays was also discussed by the C++ standards committee: front and back Proposal for iterators Library
As it is this method more closely resembles data, which:
Returns a pointer to the block of memory containing the elements of the container.
[source]
If you're looking to avoid unnecessary copying then you'll need to return a view into the data. You can either provide a front_begin() and front_end() set of functions:
const char *front_begin() const
{
return data + offset;
}
const char *front_end() const
{
return data + offset + length;
}
Or write a wrapper class:
class Data
{
private:
const char *m_Begin;
const char *m_End;
public:
Data(const char *begin, const char *end) : m_Begin(begin), m_End(end)
{
}
const char *begin() const
{
return m_Begin;
}
const char *end() const
{
return m_End;
}
}
And have your front() method return one of these:
Data front()
{
return Data(data + offset, data + offset + length)
}
If you're using C++11 then you can use a Data instance in a ranged based for loop:
Data data = buffer.front();
for(char c : data)
{
// Do something with the data
}
I get a whole lot of errors from gcc when trying to complie this method. zones_ is a
std::map<int,std::vector<Zone const * const>>
That is a private member of MyClass.
//get unique zones
std::vector<Zone const* const> MyClass::getZones() const {
std::vector<Zone const * const> zones; //why can I not do this???
std::map<Zone const * const,int> zone_set;
for(auto & pair : zones_) {
for(Zone const * const z : pair.second) {
if(zone_set.count(z) == 0) {
zone_set[z] = 1;
zones.push_back(z); //cannot do this
}
}
}
return zones;
}
Can I have a vector of const pointers to const objects?
no, the element type cannot be const (can be pointer to const type though)
also there is no need for this, since when you return a const reference to your vector, the access to the elements will be const_reference: http://www.cplusplus.com/reference/vector/vector/operator[]
No. In general, elements of vectors can't be const since they have to be moved when the vector's array needs reallocating. You'll have to store Zone const *; or perhaps use a different container type (list, deque or possibly set), if you really need constant elements.
If I have a class:
class T{
public:
int Id;
//some methods, constructor..
}
and in other class:
vector<T> collection;
and want to a write a method:
T& getValue(int Id){
//scanning the vector till i find the right value
}
The problem is that scanning the vector through iterator always give a const value so I get an error about qualifiers. So how do I get a value from a vector? but Not a const one.
EDIT: According to the Answers I tried to so something like this:
Group& Server::findGroup(unsigned int userId) const{
for(auto iterator=groups.begin();iterator!=groups.end();++iterator){
if(iterator->isInGroup(userId)){
return (*iterator);
}
}
//throw exception here
}
the definition of groups:
vector groups;
This is exactly the same example I gave at first but now T is Group.
The following code should give you a non-const iterator and work fine:
for(vector<T>::iterator i = collection.begin();
i != collection.end(); ++i) {
if(Id != i->Id)
continue;
// Matching ID! do something with *i here...
break;
}
If this doesn't help, please explain in more detail what is broken.
The problem here is the const in your declaration:
Group& Server::findGroup(unsigned int userId) const //<==THIS
That means that this is a const Server*, and thus everything in it is const as well, including groups. Which means that groups.begin() will return a const_iterator instead of an iterator.
One thing you can do (might not be a good idea; you need to be really sure!) would be to mark groups as mutable, which lets it be changed even if its enclosing object is const:
mutable vector<T> groups;
Doing this will make groups.begin() return a regular iterator.
But I would instead ask you to reevaluate why this method is declared const at all, since you're returning part of the object in a form that can be changed and thus you're not really honoring const.
I'm writing a function that will separate a vector of objects into two vectors depending on the value of one of their objects. I want it then to return whichever of the vectors.
This is the code I have so far
std::vector<AggregatedQuoteType> OrderBook::get_aggregated_order_book(SellBuyType which_side) const
{
std::vector<AggregatedQuoteType> ret;
std::vector<AggregatedQuoteType>::const_iterator i = v_OrderInfo.begin();
for (; i != v_OrderInfo.end(); ++i)
((*i).get_SB_type()==BUY ? v_BuyOrders : v_SellOrders).push_back((*i));
if(which_side==SELL){
ret = v_SellOrders;
}
else{
ret = v_BuyOrders;
}
return ret;
}
EDIT
I'm getting the following error:
[Error] no matching function for call to 'std::vector::push_back(const AggregatedQuoteType&) const'
You have marked your function get_aggregated_order_book as const.
OrderBook::get_aggregated_order_book(SellBuyType which_side) const
^^^^^
Here!
The const keyword in C++ implies that you will not be making changes to any members in your class, which I presume v_BuyOrders and v_SellOrders are.
If you're modifying the members of your OrderBook class, you need to make the method non-const.
Do you need the v_BuyOrders and v_SellOrders populated at all or just return whatever matches which_side? If the latter, how about just applying a copy_if operation and return the result?
std::vector<AggregatedQuoteType> ret;
std::copy_if(v_OrderInfo.cbegin(), v_OrderInfo.cend(), std::back_inserter(ret),
[=](const AggregatedQuoteType &at) { return at.get_SB_type() == which_side) };
return ret;
EDIT: not using lambda/C++11,
struct pred {
SellBuyType type;
pred(SellBuyType t) : type(t) {}
bool operator()(const AggregatedQuoteType &at) {
return at.get_SB_type() != type; // Copies the elements for which this returns false
}
};
std::remove_copy_if(v_OrderInfo.cbegin(), v_OrderInfo.cend(), std::back_inserter(ret), pred(which_side));
Do note however that remove_if/remove_copy_if doesn't actually remove anything, just shifts the "removed" elements to the back of the vector. If you want to remove the elements as well use vector::erase on the return value of remove_copy_if.
I'm doing a main.cpp to test my implementation of a sparse matrix, where I create two const_iterators:
SparseMatrix<double>::const_iterator a,b;
a=mata.begin(); //mata previously created as SparseMatrix<double>
b=mata.end();
... //code goes on
The problem is that it doesn't call begin and end (it doesn't even do the initial cout), but if I create two iterators it works.
Here's how I implemented begin and end for const_iterators.
const_iterator begin() const
{
cout<<"Begin"<<endl;
int minr=minRow();
int minc=minCol(findRow(minr));
mcol * mc=findCol(findRow(minr),minc);
const_iterator x;
if(mc!=NULL)
{
T* dato=&(mc->data);
x= const_iterator(genElement(minr,minc,dato));
}
else
{
x=const_iterator(NULL);
}
x.setSM(const_cast<SparseMatrix<T>*>(this));
return x;
}
const_iterator end() const
{
cout<<"End"<<endl;
const_iterator x= const_iterator(NULL);
x.setSM(const_cast<SparseMatrix<T>*>(this));
return x;
}
A strange thing I noticed is that if I create two const_iterators inside a SparseMatrix's class method, they work.
As you say, "mata [sic] previously created as SparseMatrix<double>", but your begin and end that you show are marked const. For those const member functions to be called, the object meta must be const, else the non-const versions of begin and end will be called.