I've got two classes, one Measurement class which contains a vector<double> and an Experiment which contains a vector<Measurement>:
class Measurement {
private:
vector<double> dataSet;
string time_stamp;
public:
const vector<double>& getDataSet() {return dataSet;}
string getTimeStamp() {return time_stamp;}
};
class Experiment {
private:
vector<Measurement> measurements;
public:
const vector<Measurement>& getMeasurements() {return measurements;}
};
In short, I am trying to iterate through each Measurement in an Experiment (outer loop), and then through the data in each Measurement (inner loop).
I found some help at this thread, which is why the member functions getDataSet() and getMeasurements() are const & (to make the iterators compatible).
However when I try to call iter_meas->getDataSet(), I run into trouble.
int main() {
Experiment expTest(); // experiment already filled with measurements
vector<Measurement>::const_iterator iter_meas;
vector<double>::const_iterator iter_data;
for (iter_meas = expTest.getMeasurements().begin(); iter_meas != expTest.getMeasurements().end(); iter_meas++) {
cout << iter_meas->getTimeStamp() << ','; //this works fine :)
for (iter_data = iter_meas->getDataSet().begin(); iter_data != iter_meas->getDataSet().end(); iter_data++){
// ^^^ here I get the error
// "the object has qualifiers that are not compatible with the member function getDataSet"
// "object type is const measurement"
cout << iter_data << ",";
}
cout << '\n';
}
return 0;
}
I understand that iter_meas represents a const Measurement, but I'm not sure why I can't called Measurement:: member functions on it?
I'm using Visual Studio Community 2015.
It means that the function returns const value, nothing more.
const int foo();
If the variable is const, it can't change value, it only can be initialized.
So if you have const iterator in your program, it can't change value of your object. With the methods is the same situation, it can't call methods that may change the value.
There is a solution. You have to declare your function as const (not returning const value)
int foo() const;
Now you can call this function from const iterator.
Declare it as:
const vector<Measurement>& getMeasurements() const {return measurements;}
const vector<double>& getDataSet() const {return dataSet;} // same problem
The const_iterator iter_meas represents a reference to a constant value (const Measurement&) and prevents modification of the referenced value. This particularly means that you cannot call non-static methods on such a value if the method itself is not declared as const. Such a const-declaration expresses that the method will not alter it's object.
In your code, you call iter_meas->getDataSet(), where this non-static method is declared as const vector<double>& getDataSet(), i.e. not as const which would be declared as const vector<double>& getDataSet() const. Note the const at the end of the declaration.
So with the following changes, this issue should be solved:
const vector<double>& getDataSet() const {return dataSet;}
string getTimeStamp() const {return time_stamp;}
The method getDataSet is not marked const. Since you have the method returning a const reference to the vector the method itself needs to be const, i.e. disallow mutation of the vector.
So, your method should be declared as:
const vector<double>& getDataSet() const;
Same for getMeasurements.
Also, if you're using C++ 11, maybe you'd like to use the range based for loop.
Related
#include <iostream>
#include <set>
using namespace std;
class StudentT {
public:
int id;
string name;
public:
StudentT(int _id, string _name) : id(_id), name(_name) {
}
int getId() {
return id;
}
string getName() {
return name;
}
};
inline bool operator< (StudentT s1, StudentT s2) {
return s1.getId() < s2.getId();
}
int main() {
set<StudentT> st;
StudentT s1(0, "Tom");
StudentT s2(1, "Tim");
st.insert(s1);
st.insert(s2);
set<StudentT> :: iterator itr;
for (itr = st.begin(); itr != st.end(); itr++) {
cout << itr->getId() << " " << itr->getName() << endl;
}
return 0;
}
In line:
cout << itr->getId() << " " << itr->getName() << endl;
It give an error that:
../main.cpp:35: error: passing 'const StudentT' as 'this' argument of 'int StudentT::getId()' discards qualifiers
../main.cpp:35: error: passing 'const StudentT' as 'this' argument of 'std::string StudentT::getName()' discards qualifiers
What's wrong with this code? Thank you!
The objects in the std::set are stored as const StudentT. So when you try to call getId() with the const object the compiler detects a problem, mainly you're calling a non-const member function on const object which is not allowed because non-const member functions make NO PROMISE not to modify the object; so the compiler is going to make a safe assumption that getId() might attempt to modify the object but at the same time, it also notices that the object is const; so any attempt to modify the const object should be an error. Hence compiler generates an error message.
The solution is simple: make the functions const as:
int getId() const {
return id;
}
string getName() const {
return name;
}
This is necessary because now you can call getId() and getName() on const objects as:
void f(const StudentT & s)
{
cout << s.getId(); //now okay, but error with your versions
cout << s.getName(); //now okay, but error with your versions
}
As a sidenote, you should implement operator< as :
inline bool operator< (const StudentT & s1, const StudentT & s2)
{
return s1.getId() < s2.getId();
}
Note parameters are now const reference.
Member functions that do not modify the class instance should be declared as const:
int getId() const {
return id;
}
string getName() const {
return name;
}
Anytime you see "discards qualifiers", it's talking about const or volatile.
Actually the C++ standard (i.e. C++ 0x draft) says (tnx to #Xeo & #Ben Voigt for pointing that out to me):
23.2.4 Associative containers
5 For set and multiset the value type
is the same as the key type. For map
and multimap it is equal to pair. Keys in an associative
container are immutable.
6 iterator of
an associative container is of the
bidirectional iterator category. For
associative containers where the value
type is the same as the key type, both
iterator and const_iterator are
constant iterators. It is unspecified
whether or not iterator and
const_iterator are the same type.
So VC++ 2008 Dinkumware implementation is faulty.
Old answer:
You got that error because in certain implementations of the std lib the set::iterator is the same as set::const_iterator.
For example libstdc++ (shipped with g++) has it (see here for the entire source code):
typedef typename _Rep_type::const_iterator iterator;
typedef typename _Rep_type::const_iterator const_iterator;
And in SGI's docs it states:
iterator Container Iterator used to iterate through a set.
const_iterator Container Const iterator used to iterate through a set. (Iterator and const_iterator are the same type.)
On the other hand VC++ 2008 Express compiles your code without complaining that you're calling non const methods on set::iterators.
Let's me give a more detail example. As to the below struct:
struct Count{
uint32_t c;
Count(uint32_t i=0):c(i){}
uint32_t getCount(){
return c;
}
uint32_t add(const Count& count){
uint32_t total = c + count.getCount();
return total;
}
};
As you see the above, the IDE(CLion), will give tips Non-const function 'getCount' is called on the const object. In the method add count is declared as const object, but the method getCount is not const method, so count.getCount() may change the members in count.
Compile error as below(core message in my compiler):
error: passing 'const xy_stl::Count' as 'this' argument discards qualifiers [-fpermissive]
To solve the above problem, you can:
change the method uint32_t getCount(){...} to uint32_t getCount() const {...}. So count.getCount() won't change the members in count.
or
change uint32_t add(const Count& count){...} to uint32_t add(Count& count){...}. So count don't care about changing members in it.
As to your problem, objects in the std::set are stored as const StudentT, but the method getId and getName are not const, so you give the above error.
You can also see this question Meaning of 'const' last in a function declaration of a class? for more detail.
So I defined a class on C++ that inherits from a vector of pointers:
class SuperBinList : public std::vector<SuperBin*>{
public:
SuperBinList();
SuperBinList(const std::vector<SuperBin*>& superBinList);
virtual ~SuperBinList();
SuperBinList& operator += (SuperBin* superBin);
SuperBinList& operator += (const SuperBinList& superBin);
void sortByZbi(const double sys);
void sortBySoverB() const;
};
The SuperBin class itself is defined as:
class SuperBin{
public:
SuperBin(const VI index, const double nSig, const double nBkg, const VS mPerpLabel, const VS rIsrLabel, const VS visLabel);
virtual ~SuperBin();
VI getIndex();
double getNsig();
double getNbkg();
double getSoverB();
double getBinZbi(const double sys);
VS getMperpLabel();
VS getRisrLabel();
VS getVisLabel();
SuperBin* tryMerge(SuperBin* superBin, double sys);
private:
VI index_;
double nSig_;
double nBkg_;
double sOverB_;
VS mPerpLabel_;
VS rIsrLabel_;
VS visLabel_;
};
Now the problem I'm having is that I want the SuperBinList class to be able to sort itself in descending order in terms of any of the (double type) members of the SuperBin class (such as sOverB). For this I tried the following method using a lambda function:
void SuperBinList::sortBySoverB() const{
std::sort(this->begin(), this->end(), [](const SuperBin* lhs, const SuperBin* rhs){
return lhs->getSoverB() < rhs->getSoverB();});
}
The top error I'm getting is:
error: passing 'const SuperBin' as 'this' argument discards qualifiers [-fpermissive]
return lhs->getSoverB() < rhs->getSoverB();});
Which, as I understood from similar threads, has to do with the const specifiers. However, I'm still not sure what it is that I am doing wrong. Any help would be greatly appreciated. Please pardon my ignorance as I am a physics PhD and not a computer scientist.
You have your SuperBinList::sortBySoverB() function defined as const, which means it is not allowed to modify SuperBinList, and
this will have the type const SuperBinList *, rather than SuperBinList *
Similarly, your lambda is define with const pointers const SuperBin* lhs, meaning you can only call const functions.
Change your function definitions to void SuperBinList::sortBySoverB() {} and double getSoverB() const {}, and it should compile. (Generally member functions should be marked const if they are read-only operations like getters).
#include <iostream>
#include <set>
using namespace std;
class StudentT {
public:
int id;
string name;
public:
StudentT(int _id, string _name) : id(_id), name(_name) {
}
int getId() {
return id;
}
string getName() {
return name;
}
};
inline bool operator< (StudentT s1, StudentT s2) {
return s1.getId() < s2.getId();
}
int main() {
set<StudentT> st;
StudentT s1(0, "Tom");
StudentT s2(1, "Tim");
st.insert(s1);
st.insert(s2);
set<StudentT> :: iterator itr;
for (itr = st.begin(); itr != st.end(); itr++) {
cout << itr->getId() << " " << itr->getName() << endl;
}
return 0;
}
In line:
cout << itr->getId() << " " << itr->getName() << endl;
It give an error that:
../main.cpp:35: error: passing 'const StudentT' as 'this' argument of 'int StudentT::getId()' discards qualifiers
../main.cpp:35: error: passing 'const StudentT' as 'this' argument of 'std::string StudentT::getName()' discards qualifiers
What's wrong with this code? Thank you!
The objects in the std::set are stored as const StudentT. So when you try to call getId() with the const object the compiler detects a problem, mainly you're calling a non-const member function on const object which is not allowed because non-const member functions make NO PROMISE not to modify the object; so the compiler is going to make a safe assumption that getId() might attempt to modify the object but at the same time, it also notices that the object is const; so any attempt to modify the const object should be an error. Hence compiler generates an error message.
The solution is simple: make the functions const as:
int getId() const {
return id;
}
string getName() const {
return name;
}
This is necessary because now you can call getId() and getName() on const objects as:
void f(const StudentT & s)
{
cout << s.getId(); //now okay, but error with your versions
cout << s.getName(); //now okay, but error with your versions
}
As a sidenote, you should implement operator< as :
inline bool operator< (const StudentT & s1, const StudentT & s2)
{
return s1.getId() < s2.getId();
}
Note parameters are now const reference.
Member functions that do not modify the class instance should be declared as const:
int getId() const {
return id;
}
string getName() const {
return name;
}
Anytime you see "discards qualifiers", it's talking about const or volatile.
Actually the C++ standard (i.e. C++ 0x draft) says (tnx to #Xeo & #Ben Voigt for pointing that out to me):
23.2.4 Associative containers
5 For set and multiset the value type
is the same as the key type. For map
and multimap it is equal to pair. Keys in an associative
container are immutable.
6 iterator of
an associative container is of the
bidirectional iterator category. For
associative containers where the value
type is the same as the key type, both
iterator and const_iterator are
constant iterators. It is unspecified
whether or not iterator and
const_iterator are the same type.
So VC++ 2008 Dinkumware implementation is faulty.
Old answer:
You got that error because in certain implementations of the std lib the set::iterator is the same as set::const_iterator.
For example libstdc++ (shipped with g++) has it (see here for the entire source code):
typedef typename _Rep_type::const_iterator iterator;
typedef typename _Rep_type::const_iterator const_iterator;
And in SGI's docs it states:
iterator Container Iterator used to iterate through a set.
const_iterator Container Const iterator used to iterate through a set. (Iterator and const_iterator are the same type.)
On the other hand VC++ 2008 Express compiles your code without complaining that you're calling non const methods on set::iterators.
Let's me give a more detail example. As to the below struct:
struct Count{
uint32_t c;
Count(uint32_t i=0):c(i){}
uint32_t getCount(){
return c;
}
uint32_t add(const Count& count){
uint32_t total = c + count.getCount();
return total;
}
};
As you see the above, the IDE(CLion), will give tips Non-const function 'getCount' is called on the const object. In the method add count is declared as const object, but the method getCount is not const method, so count.getCount() may change the members in count.
Compile error as below(core message in my compiler):
error: passing 'const xy_stl::Count' as 'this' argument discards qualifiers [-fpermissive]
To solve the above problem, you can:
change the method uint32_t getCount(){...} to uint32_t getCount() const {...}. So count.getCount() won't change the members in count.
or
change uint32_t add(const Count& count){...} to uint32_t add(Count& count){...}. So count don't care about changing members in it.
As to your problem, objects in the std::set are stored as const StudentT, but the method getId and getName are not const, so you give the above error.
You can also see this question Meaning of 'const' last in a function declaration of a class? for more detail.
This question already has answers here:
problem sorting using member function as comparator
(9 answers)
Closed 7 years ago.
I have something like this:
class Bar
{
public:
pair<string,string> one;
std::vector<string> cars;
Bar(string one, string two, string car);
};
class Car
{
public:
string rz;
Bar* owner;
Car(string car, Bar* p);
};
class Foo
{
public:
Foo ( void );
~Foo ( void );
int Count ( const string & one, const string & two) const;
int comparator (const Bar & first, const Bar & second) const;
std::vector<Bar> bars;
};
int Foo::comparator(const Bar & first, const Bar & second) const{
return first.name < second.name;
}
int Foo::Count ( const string & one, const string & two ) const{
int result=0;
Bar mybar = Bar( one, two, "" );
std::vector<Bar>::iterator ToFind = lower_bound(bars.begin(), bars.end(), mybar, comparator);
if (ToFind != bars.end() && ToFind->one == mybar.one ){
result = ...
}
return result;
}
The method Foo::Count should use std::lower_bound() to find element in vector<Bar> according to pair of two strings.
Now the part which doesn't work. To lower_bound() I'm providing method comparator(). I thought it was okay, but g++ says:
c.cpp: In member function ‘int Foo::Count(const string&, const string&) const’:
c.cpp:42:94: error: invalid use of non-static member function
std::vector<Bar>::iterator ToFind = lower_bound(bars.begin(), bars.end(), mybar, comparator);
And the method Count() must stay const...
I'm quite new to C++ because I'm forced to learn it.
Any ideas?
The simplest fix is to make the comparator function be static:
static int comparator (const Bar & first, const Bar & second);
^^^^^^
When invoking it in Count, its name will be Foo::comparator.
The way you have it now, it does not make sense to be a non-static member function because it does not use any member variables of Foo.
Another option is to make it a non-member function, especially if it makes sense that this comparator might be used by other code besides just Foo.
You must make Foo::comparator static or wrap it in a std::mem_fun class object. This is because lower_bounds() expects the comparer to be a class of object that has a call operator, like a function pointer or a functor object. Also, if you are using C++11 or later, you can also do as dwcanillas suggests and use a lambda function. C++11 also has std::bind too.
Examples:
// Binding:
std::lower_bounds(first, last, value, std::bind(&Foo::comparitor, this, _1, _2));
// Lambda:
std::lower_bounds(first, last, value, [](const Bar & first, const Bar & second) { return ...; });
You shall pass a this pointer to tell the function which object to work on because it relies on that as opposed to a static member function.
I have a class which has a member of type std:vector
private:
std::vector<int> myVector;
I have created Get method to access myVector
1. const std::vector<int> GetMyVector() const;
2. const void GetMyVector(std::vector<int>& vec) const;
The implementations are as follows respectively:
1. const std::vector<int> MyClass::GetMyVector() const
{
return myVector;
}
2. const void MyClass::GetMyVector(std::vector<int>& vec) const
{
vec = myVector;
}
Which one of the two Get methods is better and why?
I'd prefer option 3:
const std::vector<int>& MyClass::GetMyVector() const
{
return myVector;
}
Your option 1 returned a copy of myVector. This returns a const (so read-only) reference to the class member.
Why return the vector at all?
int MyClass::GetItem(const size_t index) const
{
return myVector[index];
}
First of all you are exposing your implementation when you return a private member of a class from a member function, which usually is bad design. Take a look at #JoachimPileborg's solution for an example of how to avoid this.
If you want to return a copy then you should return by value.
If you want to return a reference to an object then return by reference. However, bear in mind that when the object is destructed you will end up with a dangling reference, e.g.
class Foo {
public:
std::vector<int>& getVec() {
return myVec;
}
private:
std::vector<int> myVec;
};
int main() {
Foo* f = new Foo();
std::vector<int>& myRef = f->getVec();
delete f;
std::cout << myRef.size(); // The demons come! Dangling reference!
}
Because of this, it is often the right thing to return a copy instead of a reference.
If you are returning an object by copy then there is no any sense to declare it as const. So instead of
const std::vector<int> MyClass::GetMyVector() const
{
return myVector;
}
I would write
std::vector<int> MyClass::GetMyVector() const
{
return myVector;
}
The second declaration is worst than the first one because it only confuses users. It is not clear whether the corresponding data member of the class is assigned to the parameter or this method makes some changes of the parameter without assigning the corresponding data member to the parameter.
So considering suggested variants by you I would choise declaration
std::vector<int> MyClass::GetMyVector() const
{
return myVector;
}
As a rule of thumb always try to return const reference of a class member .
So use const std::vector<int> & MyClass::GetMyVector() const