I have a template class, called OneCell, here the definition:
template <class T>
class OneCell
{
.....
}
I have a cast operator from OneCell to T, here
operator T() const
{
getterCount++;
return value;
}
As you see, I want to increment variable in this method, but i get an error because the keyword const.
On the other hand, if I remove this keyword, the casting doesn't work at all.
What can I do?
Thank you, and sorry about my poor English.
Actually operator T() const is a const-member function, inside which this pointer refers to a const object, which in turns makes the getterCount const as well.
The solution is to declare getterCount as mutable, as shown below:
mutable size_t getterCount;
Now getterCount can be incremented in const member function, which also means, it can be incremented even if the object is const:
void f(OneCell const & cell)
{
//incrementing getterCount!
++cell.getterCount; //ok, even though cell is const!
}
As you see, I want to increment variable in this method, but i get an error because the keyword const. On the other hand, if I remove this keyword, the casting doesn't work at all.
It will work, but only on mutable instances of OneCell.
What can I do?
Go mutable, assuming that you change the bitwise constness but not the logical constness.
mutable int getterCount;
Related
If you notice in the following functions they both have the same for loop that searches for a integer location. Pop() compiles but I get an error for top() having to do with the const qualifiers. The heap class inherits from eecs281heap which stores a functor Comp compare where Comp is the typename. The instructor told us the only way to access the functor is through this->() so i'm just lookin for some guidance here. Thanks
error: passing ‘const larger’ as ‘this’ argument of ‘bool larger::operator()(int, int)’ discards qualifiers
This happens after running the following in int main. Through testing I already know the constructor works properly.
vector <int> data={10,2,13};
poorman_heap<int,larger> y(data.begin(),data.end());
template<typename TYPE, typename COMP>
void poorman_heap<TYPE, COMP>::pop() {
int location=0;
for(int i=1;i<data.size();i++){
if(this->compare(data.at(i),data.at(location))){
location=i;
}
}
data.erase(data.begin()+location);
return;
}
template<typename TYPE, typename COMP>
const TYPE& poorman_heap<TYPE, COMP>::top() const {
int location=0;
for(int i=1;i<data.size();i++){
if(this->compare(data.at(i),data.at(location))){
location=i;
}
}
return data.at(location);
}
P.S. greater is
struct greater{
bool operator()(int x,int y){
return x>y;
}
}
Make the call operator of greater a const operator:
struct greater
{
bool operator()(int x,int y) const
{
return x>y;
}
}
The same applies to whatever this->compare resolves to. It needs to be const.
It doesn't make much sense for a comparator to be non-const.
It looks like the problem is that the compare member has operator() declared as a non-const function. Since it sounds like you don't have the ability to change that, you might be able to get the behavior that you want by declaring it as a mutable member in poorman_heap.
The mutable keyword lets you distinguish between an object being "physically const" (meaning the actual bytes don't change and being "logically const" (meaning the bytes might change but the value of the object isn't different in a fundamental sense). Basically it means that something "doesn't count" for the purposes of const-ness. The classic example in my mind is lazy initialization - you want to declare the get_value() function on a class const, but you also don't want to waste time computing the value if no one uses it, so you declare the value mutable and now you're allowed to calculate it and assign to it inside get_value() even though it is a const member function.
I have a problem providing the correct overloading for const and not const getter functions with the new return value syntax.
In my class PhysicalNode I have defined a getter function with the new return value syntax. This is needed as the return type of the getter depends on the type of the member.
class PhysicalNode {
private:
solver::EnergySolver energySolver_; ///< The energy solver of this node
//solver::EnergyMomentumSolver energySolver_
public:
auto getEnergySolver()-> typename
std::add_lvalue_reference<decltype(PhysicalNode::energySolver_)>::type;
}
However I want now to also provide this method as const.
Normally I would use function overloading to define my const and not const getter function like that.
class PhysicalNode {
private:
solver::EnergySolver energySolver_;
public:
const solver::EnergySolver& getEnergySolver() const;
solver::EnergySolver& getEnergySolver();
}
I have tried the following function declaration but it does not work:
const auto getEnergySolver() const-> typename
std::add_lvalue_reference<decltype(PhysicalNode::energySolver_)>::type;
The compile error is:
PhysicalNode.cpp:72: error: invalid initialization of reference of type
'std::__add_lvalue_reference_helper<LbmLib::solver::EnergySolver, true,
false>::type {aka LbmLib::solver::EnergySolver&}' from expression of type
'const LbmLib::solver::EnergySolver'
How do I need to define the function declaration to define this function as constant.
If you really really want to use this notation and the standard type traits, you should write your const overload this way:
auto getEnergySolver() const ->
std::add_lvalue_reference<
std::add_const<decltype(PhysicalNode::energySolver_)>::type
// ^^^^^^^^^^^^^^
>::type;
Otherwise you would be returning a reference to non-const, which is clearly wrong considering your member function is const-qualified.
Notice, however, that type traits are not really needed here (if EnergySolver is just a regular type and not a reference type alias):
auto getEnergySolver()-> decltype(PhysicalNode::energySolver_)&;
auto getEnergySolver() const -> decltype(PhysicalNode::energySolver_) const&;
But even decltype is unnecessary. If your real program is not more complicated than the example you are showing, this is enough:
auto getEnergySolver()-> solver::EnergySolver&;
auto getEnergySolver() const -> solver::EnergySolver const&;
It's unclear why the "traditional" method is not good for your purposes.
The machination with decltype and add/remove trickery is to cover ground for templates that want to be overly generic. Where you must deal with unknown types and ones you have no control over.
For the usual situations it is way clearer to just add a few typedefs and use those directly.
For your attempt, IMO you use it incorrectly, either try const auto& in front, or auto in front and assemble the type all the way after the ->, combininge remove_reference, then add_const then add_lvalue_reference, it may work, though would make head spin.
decltype(PhysicalNode::energySolver_) is plain EnergySolver, not const EnergySolver even though the method is const, since the expression doesn't use this, which is what that const qualifier really affects. And you're not allowed to use decltype(this->energySolver_) in this context since PhysicalNode is not yet complete.
You'll have to do something like -> const decltype(PhysicalNode::energySolver_)&; or -> std::add_lvalue_reference<std::add_const<decltype(PhysicalNode::energySolver_)>::type>::type; or something in between.
While writing the following function abs, I get the error:
non-member function unsigned int abs(const T&) cannot have cv-qualifier.
template<typename T>
inline unsigned int abs(const T& t) const
{
return t>0?t:-t;
}
After removing the const qualifier for the function there is no error. Since I am not modifying t inside the function the above code should have compiled. I am wondering why I got the error?
Your desire not to modify t is expressed in const T& t. The ending const specifies that you will not modify any member variable of the class abs belongs to.
Since there is no class where this function belongs to, you get an error.
The const modifier at the end of the function declaration applies to the hidden this parameter for member functions.
As this is a free function, there is no this and that modifier is not needed.
The t parameter already has its own const in the parameter list.
The cv-qualifier on a member function specifies that the this pointer is to have indirected type const (or volatile, const volatile) and that therefore the member function can be called on instances with that qualification.
Free functions (and class static functions) don't have a this pointer.
As we all know, const keyword followed after the argument list indicates that this is a pointer to a pointer constant.
There is a non-member function, it does not belong to the class, so add const opposite end error occurs.
Solution to the problem: is to either become a class member function or remove the const keyword const opposite end
Following is the test code:
struct A
{
operator int ();
operator int () const;
};
void foo (const int);
Now, upon invoking:
foo(A()); // calls A::operator int()
Why does it always chooses the non-const version ? Even making operator const int () const; doesn't have any effect on invoking foo(). Apart from standard reference, can someone explain logically, the reason behind it ?
A() gives you a temporary A object that is not const-qualified. The A() expression is an rvalue expression, yes, but that does not make the A object const-qualified.
Since the A object is not const-qualified, the non-const operator int() is an exact match and the const operator int() requires a qualification conversion, so the non-const overload is selected as the best match.
If you want it to be const-qualified, you'd need to explicitly request a const-qualified A:
foo(identity<const A>::type());
where identity is defined as
template <typename T>
struct identity { typedef T type; };
Note that there is really no difference between operator const int() const and operator int() const: the result is an rvalue and only class-type rvalues can be const-qualified (int is not a class type).
Note also that there is no difference between the void foo(const int) that you have and void foo(int). Top-level const-qualifiers on parameter types do not affect the type of the function (i.e., the type of both of those declarations is void foo(int)). Among other reasons, this is because it doesn't matter to the caller whether there is a top-level const-qualifier; it has to make a copy regardless. The top-level const-qualifier affects only the definition of the function.
James McNellis’ answer really covered it all, but it doesn’t hurt (I hope) with more explanations.
So.
When you call …
o.operator int()
… then the overload selection depends entirely on the constness of o.
Nothing else.
To see why, consider this class:
struct Bar
{
void f() {}
void f() const {}
};
Technically those member functions do not need to be member functions. They could just as well have been chosen to be free standing functions. But then they need Bar argument:
struct Bar
{};
void f( Bar& ) {}
void f( Bar const& ) {}
And hopefully now it's easier to see that when you do
Bar o;
f( o );
then the first function can be selected. And so it is. Because if the second function was selected, then you could never get the first one. Because if you make the object const, then it would break const correctness to select the first one. So when the object is const only the second can be selected, hence, when it is not const the first one is selected.
In short, the only practical alternative to this rule would be to always select the second one, which would make the first one rather useless, yes?
Cheers & hth.,
One rule you have to remember about C++: it never takes into account the value that is being returned when it selects an overload. In this case since the operator int function takes no parameters, it can't use the parameter list to narrow down the choices either. All it can use it the constness of the object that it's being called from. Since this is a new temporary object, it's not const, so it doesn't choose the const overload.
Just curious on why a param has to be a const in operation overloading
CVector& CVector::operator= (const CVector& param)
{
x=param.x;
y=param.y;
return *this;
}
couldn't you have easily done something like this ??
CVector& CVector::operator= (CVector& param) //no const
{
x=param.x;
y=param.y;
return *this;
}
Isn't when something becomes a const, it is unchangeable for the remainder of the applications life ?? How does this differ in operation overloading ???
You don't need const:
#numerical25: Just curious on why a param has to be a const in operation overloading
It's not required, but it is a good design decision.
See the C++ standard Section 12.8-9:
A user-declared copy assignment
operator X::operator= is a non-static
non-template member function of class
X with exactly one parameter of type
X, X&, const X&, volatile X& or const
volatile X&
I think it's a good idea though:
Using a const parameter does seems like a logical design decision to me though because you want to ensure that the other value will not be changed.
It tells other people that use your class that you will not be changing the other value when you say something like: myObject = other; and it enforces this so you can't accidentally change other.
Also if you allowed non const references to the object as the parameter, then you are limiting the amount of objects that can use your function. If it is const it can be used for parameters that are const and non const. If your parameter is non const it can only be used by parameters that are non const.
const only applies to the current reference, not the object:
#numerical25: Isn't when something becomes a const, it is unchangeable for the remainder of the applications life ?? How does this differ in operation overloading ???
A const reference is simply that a reference that is const. It does not change the const-ness of the actual object you are passing in.
An example of non-const operator overloading:
Here is an example of operator overloading where the parameter is not const.
I DO NOT RECOMMEND TO DO THIS THOUGH:
class B
{
public:
const B& operator=(B& other)
{
other.x = 3;
x = other.x;
return *this;
}
int x;
};
void main(int argc, char** argv[])
{
B a;
a.x = 33;
B b;
b.x = 44;
a = b;//both a and b will be changed
return 0;
}
A const parameter is const throughout the function using it, it does not change its constness outside of it.
In this case you want to declare a const argument so that your assignment operator accepts both non-const variables and const variables; the latter case, in particular, includes the result of expressions, which is a temporary const variable and which you generally want to support in assignments.
If you used
CVector& CVector::operator= (CVector& param) // no const
then did this:
const CVector& my_vector = GetMyVector();
some_other_vector = my_vector; // call assignment operator - error!
You'll get an error because my_vector is a const CVector& and that can't be cast to a CVector& (non-const reference). It's just the local reference to it inside the operator= function that is const, not the entire object itself.
You can use the non-const variety, but this has two repercussions, one which is functional, and one which is about what you, as the writer of the function, are telling the user.
1) people calling the function that takes a non-const reference would not be able to call it using a const variable
2) when you have a function argument that's a non-const reference, you're signalling, "I reserver the right to change this". Typically, when a user of your function writes a = b;, he doesn't expect b to change.
Note that there's a third option you could use for this, pass-by-value:
CVector& CVector::operator= (CVector param) //no reference
This doesn't have either of the problems I mention above. However, it's very inefficient. Because of these three factors, passing by reference-to-const is preferred, especially in cases like a vector where copying can be expensive.
For the same reason you would use const anywhere: to ensure that future changes to the method don't inadvertently modify the passed in parameter, to help document the interface to notify callers that it is safe to pass param without risk of it changing, and to allow callers to pass in references that are declared as const in the calling code.
Another reason is to allow for conversions. For example:
string s = "foo";
s = "bar";
Here, an implementation might choose to only provide the assignment operator that takes a const reference to a string as a parameter, and depend on the compiler using a constructor to create a temporary string from the char * "bar". This would not work if the op='s parameter was not const, as you cannot bind a temporary to a non-const reference.
The const qualifier makes the passed parameter (in your example it is 'const CVector& param') as read only. The const qualifier ensures that the parameter (param) is not altered inside the operator=() method.
Without the const qualifier, the following is possible:
CVector& CVector::operator= (CVector& param)
{
x=param.x;
y=param.y;
param.x = 10; // some random value
param.y = 100;
return *this;
}
The above method alters the right hand side operand 'param' after assigning the value to the left hand side operand. The const qualifier helps you not to violate the semantics of the assignment operation.