I was surprised to find this "hole" in "const"ness:
#include <stdio.h>
class A
{
int r ;
public:
A():r(0){}
void nonconst()
{
puts( "I am in ur nonconst method" ) ;
r++;
}
} ;
class B
{
A a ;
A* aPtr ;
public:
B(){ aPtr = new A() ; }
void go() const
{
//a.nonconst() ; // illegal
aPtr->nonconst() ; //legal
}
} ;
int main()
{
B b ;
b.go() ;
}
So basically from const method B::go(), you can invoke the non-const member function (aptly named nonconst()) if object of type A is referenced by a pointer.
Why is that? Seems like a problem (it kind of was in my code, where I found it.)
When and object of type B is const, then all of its members are const, which means its two members are, for the duration of B::go(), effectively
A const a;
A * const aPtr;
The first is a constant object of type A, on which you can only call const member functions. The second, however, is a constant pointer to a non-constant A. You could not legally say aPtr = <anything> from within the function B::go(), since that would modify aPtr, which is constant.
A pointer to a constant A would be declared as A const* aPtr or const A* aPtr, which would then make calling the non-constant A::nonconst() illegal.
The "const-ness" of an object does not extend to other objects through pointers. In your example, the const part is either the entire object a, or the pointer aPtr. Because aPtr is a A * and not a const A *, you are allowed to call a non-const method.
If you change
A* aPtr ;
to
const A* aPtr ;
then you will not be able to call aPtr->nonconst().
Language definition of const
I was surprised to find this "hole" in "const"ness:
There is none.
const applies uniformly to all class members: in a const member function of class C, this has type const C *, so for a member C::mem declared as type T:
class C {
// ...
T mem;
};
this->mem has type const T.
Please take type to discern what is the declared type T and the corresponding const-qualified type for all the members.
Seems like a problem (it kind of was in my code, where I found it.)
Just because the systematic application of rules does not do what you expected does not mean there is a problem with the rules, it means there is a problem with your expectations.
You should write down your expectations to see that you expected a non uniform application if const to different types.
When you program, you have to reason logically. You should infer things, not expect them when there is no logical reason.
Using const correctly
Why is that?
Your classes being called A and B, it's pretty hard to understand what constitute logical state and what does not. ;) You ask a "moral" question (not a question only on legal/illegal C++ programs), and your code fragment has no "moral" value. If you actually post relevant code, we might make some "moral" judgements about it.
Logical state
You should declare const the functions that do not change the "logical state" of the object it's applied to.
It means you have to define what is the "logical state" of your class instances: it's an abstract concept, and only you can define it, because it's a high level concept. The "logical state" relates to the problem your class should solve.
Then you can discern which variables contribute to the logical state: does *(b.aPtr) contribute to the logical state of b?
Closely related questions
Do you know about the copy constructor?
About the copy assignment operator?
About the destructor?
Related
A co-worker asked about some code like this that originally had templates in it.
I have removed the templates, but the core question remains: why does this compile OK?
#include <iostream>
class X
{
public:
void foo() { std::cout << "Here\n"; }
};
typedef void (X::*XFUNC)() ;
class CX
{
public:
explicit CX(X& t, XFUNC xF) : object(t), F(xF) {}
void execute() const { (object.*F)(); }
private:
X& object;
XFUNC F;
};
int main(int argc, char* argv[])
{
X x;
const CX cx(x,&X::foo);
cx.execute();
return 0;
}
Given that CX is a const object, and its member function execute is const, therefore inside CX::execute the this pointer is const.
But I am able to call a non-const member function through a member function pointer.
Are member function pointers a documented hole in the const-ness of the world?
What (presumably obvious to others) issue have we missed?
The constness of execute() only affects the this pointer of the class. It makes the type of this a const T* instead of just T*. This is not a 'deep' const though - it only means the members themselves cannot be changed, but anything they point to or reference still can. Your object member already cannot be changed, because references cannot be re-seated to point to anything else. Similarly, you're not changing the F member, just dereferencing it as a member function pointer. So this is all allowed, and OK.
The fact that you make your instance of CX const doesn't change anything: again, that refers to the immediate members not being allowed to be modified, but again anything they point to still can. You can still call const member functions on const objects so no change there.
To illustrate:
class MyClass
{
public:
/* ... */
int* p;
void f() const
{
// member p becomes: int* const p
*p = 5; // not changing p itself, only the thing it points to - allowed
p = NULL; // changing content of p in const function - not allowed
}
};
In this context object is a reference to a X, not a reference to a const X. The const qualifier would be applied to the member (i.e. the reference, but references can't be const), not to the referenced object.
If you change your class definition to not using a reference:
// ...
private:
X object;
// ...
you get the error you are expecting.
The instance object of class X is not const. It is merely referenced by an object which is const. Const-ness recursively applies to subobjects, not to referenced objects.
By the alternative logic, a const method wouldn't be able to modify anything. That is called a "pure function," a concept which doesn't exist in current standard C++.
You are calling foo on object, not on this.
Since object is declared as an X&, in a constant CX, it is actually an X& const (which is not the same as const X&) allowing you to call non const methods on it.
One helpful way of thinking about it might be that your X object is not a member of CX at all.
I have a class which contains references, like:
class A {
A(B &b) : b(b) {} // constructor
B &b;
}
Sometimes b must be read-only, sometimes it is writeable. When I make a const A a(b); object, it's obvious that I want to protect the data inside it as const.
But - by accident - it's easy to make a non-const copy of the object which will make the data inside it vulnerable.
const A a(b); // b object protected here
A a_non_const(a);
a_non_const.b.non_const_function(...); // b not protected now
I think that I should somehow prevent copies of the object when it is const like this:
const A a(b);
const A a2(a); // OK!
A a_non_const(a); // Compiler error
Is this possible at all?
flaw in your code: your data isn't "protected" even with const
The const type qualifier manages access to the member functions of a type as well as the access to its members. Since your member B & b is a reference, const doesn't do much for you here: A reference cannot be changed after initialization either way. How you access the target of that reference isn't even considered:
const A a(b);
a.b.non_const_function(); // OOPS, no problem!
solution with templates
Instead of (ab)using the const type qualifier you could add a "flag" to your type, to differentiate between cases where you need to be able to have non-const access and case where you don't:
#include <type_traits>
struct B {
void danger() {
}
void all_fine() const {
}
};
template<bool Writeable>
struct A {
using BRef = typename std::conditional<Writeable, B &, B const &>::type;
BRef b;
A (BRef b) : b(b) {};
};
using ConstA = A<false>;
using NonConstA = A<true>;
int main() {
B b;
ConstA a(b);
//NonConstA nc_a(a);
ConstA another_a(a);
//another_a.b.danger();
another_a.b.all_fine();
NonConstA a2(b);
a2.b.danger();
}
With some std::enable_if you can then selectively enable / disable member functions of A depending on whether they need "writeable" b or not.
real solution: refactor your design
BUT I'd like to highlight this comment even more:
"Sometimes b must be read-only, sometimes it is writeable." All your problems stem from this weird duality. I suggest picking one set of semantics for your class, not two
From Lightness Races in Orbit
You should probably instead consider splitting your class such that you have a CommonA with functionality used by both a WriteableA and a NonWriteableA (the names are terrible, but I hope you understand what I mean).
You can do it for the heap:
static const A *constCopy(const A &a); // and of course implement it somewhere
Then you will not accidentally modify the object via the pointer you get (which has to be stored in const A *, otherwise the compiler will complain).
However it will not work with stack-based objects, as returning const A & of a local variable is a rather deadly action, and "const constructor" has not been invented yet (related: Why does C++ not have a const constructor?)
Having the following object
class Parser
{
public:
Parser(ComponentFactory * const factory): _factory(factory) {};
~Parser() = default;
void parse() const {
_factory->setFoo("foo");
}
private:
Factory * _factory;
};
My function parse() is specified as const. That's to say the function shouldn't modify the current object state and performs only read-only logics.
However, does the modification of the factory object imply a change of my current object's state? In other therms, is this even compilable?
I would like to understand why if yes, since I can't find any related subject on the net..
EDIT:
Since none can understand this, let me try to explain it better. In simple words, is the above code supposed to compile?
However, does the modification of the factory object imply a change of my current object's state?
No
In other therms, is this even compilable?
Yes.
I would like to understand why if yes, since I can't find any related subject on the net..
Say you have
struct Foo
{
int* ptr;
Foo() : ptr(new int(0)) {}
// It is valid since it does not change the state of Foo.
// It does not change where ptr points to. It just changes
// the value of what ptr points to.
void set(int value) const { *ptr = value; }
};
The compiler gives you the basic const characteristics. Higher level const-ness needs to be implemented by classes themselves.
In the case of Foo, if changing the value of what ptr to is deemed to change the state of Foo by the creator of Foo, then you'll have to remove const qualifier from the member function.
Marking a function as const means that you can't:
change any of the values of this's ivars
call a non-const function on this or any of its ivars
E.g. it would treat int foo; as const int foo;
These properties aren't transitive to pointers. Factory *_factory; doesn't become const Factory *_factory;, it becomes Factory *const _factory;, which is what you already have.
Imagine if you were using a smart pointer instead, would you expect the compiler to know that it should convert std::shared_ptr<Factory> _factory; into std::shared_ptr<const Factory> _factory;? All it would do is treat it as const std::shared_ptr<Factory> _factory;
I know that a const object can't call a non-const member function. It says so in the C++14 Standard (ISO/IEC 14882:2014) Section 9.3.2, Paragraph 3:
A cv-qualified member function can be called on an object-expression (5.2.5) only if the object-expression is as cv-qualified or less-cv-qualified than the member function.
Does this constraint make sense if the member function does not modify anything? Or if the member function modifies a mutable variable? For example:
class My_Class {
public:
mutable int value;
void function( int x ) const { value = x; } // Correct
//void function( int x ) { value = x; } // Not Correct
My_Class() : value(0) {}
};
int main(){
const My_Class obj;
obj.function( 1 );
return 0;
}
In this specific case, if the function is const, the program is correct and the function can modify a variable of the const object. If the function is not const, the program is not correct. So in the end, I need to write const to be able to modify something, which should be the opposite purpose of const.
Does anyone know why this rule is designed this way?
Does [Section 9.3.2, Paragraph 3 of] make sense if the member function does not modify anything? Or if the member function modifies a mutable variable?
Yes, it does. The caller of the function cannot in general know whether the function modifies any non mutable member. It knows whether the function is const or not, so the compiler can only make a decision based on that.
A non const function could modify the non mutable state, so you obviously may not call it on a const object. Whether the non const function does modify the state is irrelevant since it is an implementation detail that is not available when deciding if the call is allowed. In other words, constness is part of the interface of the function, while the implementation of the function is not part of the interface. The interface of the function is fully specified by its declaration.
An example:
struct C {
mutable int a;
int b;
void do_something();
};
const C c;
c.do_something();
Does it make sense to allow the call do_something? Does the fact that do_something might not modify b affect that? How could we assume that do_something does not modify b?
The answers are: It wouldn't make sense. It does not have an effect. There is no way we could make such assumption.
Writing a non const member function that doesn't modify any non mutable state - at least potentially or allow such modification indirectly by returning a non const reference / pointer to this - makes little sense, although the standard does permit doing so.
So in the end, I need to write const to be able to modify something, which should be the opposite purpose of const.
That might seem oxymoronic, but that is because it is an oversimplification. You need to write const to be able to modify the mutable state of a const object. The const that you write declares that you do not modify any non mutable state, and that declaration gives the permission to call it on const objects.
Your commented-out non-const function is still correct to use with non-const objects.
There is a fundamental misunderstanding in your question:
class My_Class {
public:
mutable int value;
void function( int x ) { value = x; } // Absolutely fine
My_Class() : value(0) {}
};
mutable does not mean "can only be modified in a const member function". It means "can be modified even in a const member function".
The mutable keyword,
Applies to non-static class members of non-reference non-const type and specifies that the member does not affect the externally visible state of the class (as often used for mutexes, memo caches, lazy evaluation, and access instrumentation). mutable members of const class instances are modifiable
You're questioning the validity of a C++ keyword. I feel the examples in this definition, provide positive proof that such a keyword is needed, and that mutable as imagined by the designers of the language is a boon to us.
A co-worker asked about some code like this that originally had templates in it.
I have removed the templates, but the core question remains: why does this compile OK?
#include <iostream>
class X
{
public:
void foo() { std::cout << "Here\n"; }
};
typedef void (X::*XFUNC)() ;
class CX
{
public:
explicit CX(X& t, XFUNC xF) : object(t), F(xF) {}
void execute() const { (object.*F)(); }
private:
X& object;
XFUNC F;
};
int main(int argc, char* argv[])
{
X x;
const CX cx(x,&X::foo);
cx.execute();
return 0;
}
Given that CX is a const object, and its member function execute is const, therefore inside CX::execute the this pointer is const.
But I am able to call a non-const member function through a member function pointer.
Are member function pointers a documented hole in the const-ness of the world?
What (presumably obvious to others) issue have we missed?
The constness of execute() only affects the this pointer of the class. It makes the type of this a const T* instead of just T*. This is not a 'deep' const though - it only means the members themselves cannot be changed, but anything they point to or reference still can. Your object member already cannot be changed, because references cannot be re-seated to point to anything else. Similarly, you're not changing the F member, just dereferencing it as a member function pointer. So this is all allowed, and OK.
The fact that you make your instance of CX const doesn't change anything: again, that refers to the immediate members not being allowed to be modified, but again anything they point to still can. You can still call const member functions on const objects so no change there.
To illustrate:
class MyClass
{
public:
/* ... */
int* p;
void f() const
{
// member p becomes: int* const p
*p = 5; // not changing p itself, only the thing it points to - allowed
p = NULL; // changing content of p in const function - not allowed
}
};
In this context object is a reference to a X, not a reference to a const X. The const qualifier would be applied to the member (i.e. the reference, but references can't be const), not to the referenced object.
If you change your class definition to not using a reference:
// ...
private:
X object;
// ...
you get the error you are expecting.
The instance object of class X is not const. It is merely referenced by an object which is const. Const-ness recursively applies to subobjects, not to referenced objects.
By the alternative logic, a const method wouldn't be able to modify anything. That is called a "pure function," a concept which doesn't exist in current standard C++.
You are calling foo on object, not on this.
Since object is declared as an X&, in a constant CX, it is actually an X& const (which is not the same as const X&) allowing you to call non const methods on it.
One helpful way of thinking about it might be that your X object is not a member of CX at all.