What is mutable const? - c++

I didn't find any topics related to mutable const on SO. I have reduced code to minimal working code (on visual studio). If we uncomment //*data = 11;, the compiler complains about const-ness. I wonder how mutable const works.
class A
{
public:
void func(int & a) const
{
pdata = &a;
//*pdata = 11;
}
mutable const int * pdata;
};
int main()
{
const A obj;
int a = 10;
obj.func(a);
}

This example is a little confusing, because the mutable keyword is not part of the type specifier const int *. It's parsed like a storage class like static, so the declaration:
mutable const int *pdata;
says that pdata is a mutable pointer to a const int.
Since the pointer is mutable, it can be modified in a const method. The value it points to is const, and cannot be modified through that pointer.

You are correct in understanding that a mutable const class member is meaningless. Your example is more demonstrating a quirk of how const works with pointers.
Consider the following class.
class A {
const int * x; // x is non-const. *x is const.
int const * y; // y is non-const. *y is const.
int * const z; // z is const. *z is non-const.
};
So const has different meanings depending on where you write it.
Since x and y are non-const, there's no contradiction in making them mutable.
class A {
mutable const int * x; // OK
mutable int const * y; // OK
mutable int * const z; // Doesn't make sense
};

mutable const sounds like an oxymoron, but it actually has a perfectly sensible explanation. const int * implies that the pointed-to integer value cannot be changed through that pointer. mutable means that the pointer itself can be changed to point to another int object, even if the A object to which the pdata member belongs it itself const. Again, the pointed to value can't be changed through that pointer, but that pointer itself can be reseated.
Your code fails when the assignment statement is uncommented because that assignment violates your promise not to modify the pointed to value (the const int * part).

Related

Modyfying object when passed by const reference [duplicate]

I am working on const-correctness of my code and just wondered why this code compiles:
class X
{
int x;
int& y;
public:
X(int& _y):y(_y)
{
}
void f(int& newY) const
{
//x = 3; would not work, that's fine
y = newY; //does compile. Why?
}
};
int main(int argc, char **argv)
{
int i1=0, i2=0;
X myX(i1);
myX.f(i2);
...
}
As far as I understand, f() is changing the object myX, although it says to be const. How can I ensure my compiler complains when I do assign to y? (Visual C++ 2008)
Thank a lot!
Because you are not changing any variable in X. Actually, you are changing _y which is an outsider with respect to your class. Don't forget that:
y = newY;
Is assigning the value of newY to the variable pointed by y, but not the references them selves. Only on initialization the references are considered.
The situation is similar to pointer members. In a const member function, the const applies to the pointer itself, not the pointee.
It's the difference between:
X* const //this is how the const applies: you can modify the pointee
const X*
Except X& const isn't valid syntax, since the reference can't be made to refer to another object in the first place (they are implicitly always const). In conclusion: const on methods has no effect on member references.
As additional information for the accepted answer, I want to say, in fact one can
change variables in X.
Because you are not changing any variable in X.
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class Bar {
public:
void funcA() const {
c++;
}
int b = 3;
int &c = b;
};
int main()
{
Bar b;
b.funcA();
cout << b.b << endl; //4
}
So the main idea for this problem is:
It modifies what the member refers to, it does not modify the member.
This also applies for pointer members.
See here Why can reference members be modified by const member functions?

Understanding const

If you want to write an iterator, you usually write
T* operator->() const;
My problem is understanding this "const" with pointers and reference.
For instance, you can write the following struct:
struct A{
int* x;
A(int& x0):x{&x0}{}
int* ptr() const {return x;}
int& ref() const {return *x;}
};
And you can use it this way:
int x = 10;
const A a{x};
int& r = a.ref();
int* p = a.ptr();
*p = 4;
cout << x << endl; // prints 4
r = 25;
cout << x << endl; // prints 25
But why this compiles and works right (at least with g++ and clang). Why?
As I defined
const A a{x};
this "a" is const. So when I call
int* p = a.ptr();
I am calling ptr() with a const object, so the internal pointer A->x must be "int * const". But I am returning a "int *" without const. Why is this correct?
And what happens with the reference? If I call A::ref() with a "const A", what's the type this function returns? Something like "int& const"??? <--- I suppose this is the same as "int&".
Thanks for your help.
There is a different between bitwise const and logical const.
When you have a const A, you can't modify its int* member. But there's a difference between modifying the member itself (which int that x points to) and modifying through the member (the value of the int that x points to). Those are different kinds of const. In the simplest case:
struct Ptr { int* p; };
int i = 42;
const Ptr ptr{&i};
*ptr.p = 57;
ptr.p still points to i, nothing changed there, so the const mechanic is enforced. But it's not logically const since you still changed something through a const object. The language doesn't enforce that for you though. That's up to you as the library writer.
If you want to propagate const-ness, you just provide an interface that is logically const:
int const* ptr() const {return x;}
int const& ref() const {return *x;}
// ^^^^^
Now, users can't modify through your const A both bitwise (can't change what x points to) and logically (can't change that value of that pointee either).
But why this compiles and works right (at least with g++ and clang). Why?
Because the program is well formed and has defined behaviour. Const correctness was not violated.
I am calling ptr() with a const object, so the internal pointer A->x must be "int * const". But I am returning a "int *" without const. Why is this correct?
Because it is completely OK to make copies of const objects. Those copies need not to be const. Copying an object does not make modifications to the original object (assuming there is no user defined copy constructor that does silly things).
And what happens with the reference? If I call A::ref() with a "const A", what's the type this function returns?
int& ref() always returns int&. Just like int* ptr() always returns int*.
Something like "int& const"???
There is no such thing like int& const. References cannot have top level qualifiers (they can never be re-assigned).
In struct A, when you make an instance of it const, you make the pointer constant, but that doesn't automatically make the pointed-to object constant.
Declaring something like const A a(ref); is basically equivalent to invoking the following code:
struct A_const {
int * const x;
A(int& x0):x{&x0}{}
int* ptr() const {return x;}
int& ref() const {return *x;}
};
If you remember your pointer rules, this means that x is a constant pointer, which means it cannot be made to point to something else (it's functionally similar to a reference, but can be null), but critically, the int that it is pointing to is not constant, which means nothing stops you from writing something like this:
int val = 17;
const A a(val);
*(a.val) = 19; //Totally valid, compiles, and behaves as expected!
int val2 = 13;
//a.val = &val2; //Invalid, will invoke compile-time error
This is also the reason why std::unique_ptr<int> and std::unique_ptr<const int> represent different objects.
If you want the pointed-to object to not be modifiable on a const object, you need to enforce that in the code itself. Since functions can be overloaded on the basis of whether the source object is const or not, that's pretty easy:
struct A {
int * x;
A(int& x0):x{&x0}{}
int * ptr() {return x;}
int & ref() {return *x;}
int const* ptr() const {return x;}
int const& ref() const {return *x;}
};
int val = 17;
A a(val);
a.ref() = 19;//Okay.
*a.ptr() = 4;//Okay.
const A b(val);
b.ref() = 13;//Compile error
*b.ptr() = 17;//Compile error

Returning non-const reference from a const member function

Why does returning the reference to a pointed-to member variable work, but not the other? I know that a const member function should only return const references, but why does that not seem true for pointers?
class MyClass
{
private:
int * a;
int b;
public:
MyClass() { a = new int; }
~MyClass() { delete a; }
int & geta(void) const { return *a; } // good?
int & getb(void) const { return b; } // obviously bad
};
int main(void)
{
MyClass m;
m.geta() = 5; //works????
m.getb() = 7; //doesn't compile
return 0;
}
int & geta(void) const { return *a; } // good?
int & getb(void) const { return b; } // obviously bad
In a const-function, every data member becomes const in such way that it cannot be modified. int becomes const int, int * becomes int * const, and so on.
Since the type of a in your first function becomes int * const, as opposed to const int *, so you can change the data (which is modifiable):
m.geta() = 5; //works, as the data is modifiable
Difference between : const int* and int * const.
const int* means the pointer is non-const, but the data the pointer points to is const.
int * const means the pointer is const, but the data the pointer points to is non-const.
Your second function tries to return const int &, since the type of b become const int. But you've mentioned the actual return type in your code as int &, so this function would not even compile (see this), irrespective of what you do in main(), because the return type doesn't match. Here is the fix:
const int & getb(void) const { return b; }
Now it compiles fine!.
Because a becomes int * const a;. That is, you cannot change the value of a (change what it points at), just as const says. The const-ness of what a points at is a completely different issue.
Please see my answer here for a thorough discussion of const and const member functions.
Nawaz's answer is very good. However, the point about the compiler catching the error need not always hold. The C++ FAQ warns about it here.
The good news is that the compiler will often catch you if you get this wrong. In particular, if you accidentally return a member of your this object by non-const reference [...] the compiler will often detect it and give you a compile-time error [...].
The bad news is that the compiler won’t always catch you: there are some cases where the compiler simply won’t ever give you a compile-time error message.
Translation: you need to think. If that scares you, find another line of work; “think” is not a four-letter word.

Returning const 'this' pointer

if this is a const pointer to class's object how can you return a const pointer from non-const return type?
Class T
{
public:
T* func(){return this;}
};
Firstly, this is not a "const pointer". Where did you get that strange idea? Pointer this has scalar type and is not an lvalue, meaning that it can't possibly be "const" or "non-const". Pointer this is an rvalue and it is non-modifiable.
Secondly, the code in your question is valid regardless of whether the pointer involved is const or not. For example, the following code is valid for exactly the same reason
int *const p = 0; /* a const pointer */
int *foo() {
return p; /* OK, no error here */
}
What is returned in this case is a completely independent copy of the original pointer value. It does not matter whether p is const or not - making a copy of const value does not in any way violate its constness.
This is exactly what's done in your code sample - you are returning a copy of this's value. Why would you even care whether this is const or not?
Unless the member function is const-qualified (e.g. T* func() const instead of just T* func()), this is of type T*.
this is not itself modifiable (so you can't assign something to this), but it's just a pointer to an object of type T like any other and can be used as such.
AndreyT provided the explanation and here is the reference.
From standard docs 9.3.2.1 The this pointer,
In the body of a non-static (9.3) member function, the keyword this is an rvalue expression whose value is the address
of the object for which the function is called. The type of this in a member function of a class X is X*. If the member
function is declared const, the type of this is const X*, if the member function is declared volatile, the type of
this is volatile X*, and if the member function is declared const volatile, the type of this is const volatile
X*.
which explains the question that you asked and the comment you made to AndreyT.
Hope this one also helps.
A pointer is not a object, so it does not matter if the pointer is const when it is a return value.
Consider this:
int x;
int f() {
return x;
}
const int g() {
return x;
}
There's actually no difference between f() and g().
But it may make difference if you replace all "int" to some other name of a class.
"const" may prevent you from some error, e.g. when you don't write a copying constructor properly.
class T
{
int *x;
public:
T() {x = new int;}
void set_x(int n) {*x = n;}
int get_x() const {return *x;}
};
T a;
T f() {
return a;
}
const T g() {
return a;
}
int main() {
f().set_x(123); // The value is modified
// even though a return value is not a l-value.
g().set_x(123); // Compile Error
}
So, if you want to prevent modifying data of the object by referring to the return value, what you need is:
class T
{
public:
const T* func() const
{ return this; }
};

Const method that modifies *this without const_cast

The following pattern has arisen in a program I'm writing. I hope it's not too contrived, but it manages to mutate a Foo object in the const method Foo::Questionable() const, without use of any const_cast or similar. Basically, Foo stores a reference to FooOwner and vice versa, and in Questionable(), Foo manages to modify itself in a const method by calling mutate_foo() on its owner. Questions follow the code.
#include "stdafx.h"
#include <iostream>
using namespace std;
class FooOwner;
class Foo {
FooOwner& owner;
int data;
public:
Foo(FooOwner& owner_, int data_)
: owner(owner_),
data(data_)
{
}
void SetData(int data_)
{
data = data_;
}
int Questionable() const; // defined after FooOwner
};
class FooOwner {
Foo* pFoo;
public:
FooOwner()
: pFoo(NULL)
{}
void own(Foo& foo)
{
pFoo = &foo;
}
void mutate_foo()
{
if (pFoo != NULL)
pFoo->SetData(0);
}
};
int Foo::Questionable() const
{
owner.mutate_foo(); // point of interest
return data;
}
int main()
{
FooOwner foo_owner;
Foo foo(foo_owner, 0); // foo keeps reference to foo_owner
foo_owner.own(foo); // foo_owner keeps pointer to foo
cout << foo.Questionable() << endl; // correct?
return 0;
}
Is this defined behavior? Should Foo::data be declared mutable? Or is this a sign I'm doing things fatally wrong? I'm trying to implement a kind of lazy-initialised 'data' which is only set when requested, and the following code compiles fine with no warnings, so I'm a little nervous I'm in UB land.
Edit: the const on Questionable() only makes immediate members const, and not the objects pointed to or referenced by the object. Does this make the code legal? I'm confused between the fact that in Questionable(), this has the type const Foo*, and further down the call stack, FooOwner legitimately has a non-const pointer it uses to modify Foo. Does this mean the Foo object can be modified or not?
Edit 2: perhaps an even simpler example:
class X {
X* nonconst_this; // Only turns in to X* const in a const method!
int data;
public:
X()
: nonconst_this(this),
data(0)
{
}
int GetData() const
{
nonconst_this->data = 5; // legal??
return data;
}
};
Consider the following:
int i = 3;
i is an object, and it has the type int. It is not cv-qualified (is not const or volatile, or both.)
Now we add:
const int& j = i;
const int* k = &i;
j is a reference which refers to i, and k is a pointer which points to i. (From now on, we simply combine "refer to" and "points to" to just "points to".)
At this point, we have two cv-qualified variables, j and k, that point to a non-cv-qualified object. This is mentioned in §7.1.​5.1/3:
A pointer or reference to a cv-qualified type need not actually point or refer to a cv-qualified object, but it is treated as if it does; a const-qualified access path cannot be used to modify an object even if the object referenced is a non-const object and can be modified through some other access path. [Note: cv-qualifiers are supported by the type system so that they cannot be subverted without casting (5.2.11). ]
What this means is that a compiler must respect that j and k are cv-qualified, even though they point to a non-cv-qualified object. (So j = 5 and *k = 5 are illegal, even though i = 5 is legal.)
We now consider removing the const from those:
const_cast<int&>(j) = 5;
*const_cast<int*>(k) = 5;
This is legal (§refer to 5.2.11), but is it undefined behavior? No. See §7.1.​5.1/4:
Except that any class member declared mutable (7.1.1) can be modified, any attempt to modify a const object during its lifetime (3.8) results in undefined behavior.
Emphasis mine.
Remember that i is not const and that j and k both point to i. All we've done is tell the type system to remove the const-qualifier from the type so we can modify the pointed to object, and then modified i through those variables.
This is exactly the same as doing:
int& j = i; // removed const with const_cast...
int* k = &i; // ..trivially legal code
j = 5;
*k = 5;
And this is trivially legal. We now consider that i was this instead:
const int i = 3;
What of our code now?
const_cast<int&>(j) = 5;
*const_cast<int*>(k) = 5;
It now leads to undefined behavior, because i is a const-qualified object. We told the type system to remove const so we can modify the pointed to object, and then modified a const-qualified object. This is undefined, as quoted above.
Again, more apparent as:
int& j = i; // removed const with const_cast...
int* k = &i; // ...but this is not legal!
j = 5;
*k = 5;
Note that simply doing this:
const_cast<int&>(j);
*const_cast<int*>(k);
Is perfectly legal and defined, as no const-qualified objects are being modified; we're just messing with the type-system.
Now consider:
struct foo
{
foo() :
me(this), self(*this), i(3)
{}
void bar() const
{
me->i = 5;
self.i = 5;
}
foo* me;
foo& self;
int i;
};
What does const on bar do to the members? It makes access to them go through something called a cv-qualified access path. (It does this by changing the type of this from T* const to cv T const*, where cv is the cv-qualifiers on the function.)
So what are the members types during the execution of bar? They are:
// const-pointer-to-non-const, where the pointer points cannot be changed
foo* const me;
// foo& const is ill-formed, cv-qualifiers do nothing to reference types
foo& self;
// same as const int
int const i;
Of course, the types are irrelevant, as the important thing is the const-qualification of the pointed to objects, not the pointers. (Had k above been const int* const, the latter const is irrelevant.) We now consider:
int main()
{
foo f;
f.bar(); // UB?
}
Within bar, both me and self point to a non-const foo, so just like with int i above we have well-defined behavior. Had we had:
const foo f;
f.bar(); // UB!
We would have had UB, just like with const int, because we would be modifying a const-qualified object.
In your question, you have no const-qualified objects, so you have no undefined behavior.
And just to add an appeal to authority, consider the const_cast trick by Scott Meyers, used to recycle a const-qualified function in a non-const function:
struct foo
{
const int& bar() const
{
int* result = /* complicated process to get the resulting int */
return *result;
}
int& bar()
{
// we wouldn't like to copy-paste a complicated process, what can we do?
}
};
He suggests:
int& bar(void)
{
const foo& self = *this; // add const
const int& result = self.bar(); // call const version
return const_cast<int&>(result); // take off const
}
Or how it's usually written:
int& bar(void)
{
return const_cast<int&>( // (3) remove const from result
static_cast<const foo&>(*this) // (1) add const to this
.bar() // (2) call const version
);
}
Note this is, again, perfectly legal and well-defined. Specifically, because this function must be called on a non-const-qualified foo, we are perfectly safe in stripping the const-qualification from the return type of int& boo() const.
(Unless someone shoots themselves with a const_cast + call in the first place.)
To summarize:
struct foo
{
foo(void) :
i(),
self(*this), me(this),
self_2(*this), me_2(this)
{}
const int& bar() const
{
return i; // always well-formed, always defined
}
int& bar() const
{
// always well-formed, always well-defined
return const_cast<int&>(
static_cast<const foo&>(*this).
bar()
);
}
void baz() const
{
// always ill-formed, i is a const int in baz
i = 5;
// always ill-formed, me is a foo* const in baz
me = 0;
// always ill-formed, me_2 is a const foo* const in baz
me_2 = 0;
// always well-formed, defined if the foo pointed to is non-const
self.i = 5;
me->i = 5;
// always ill-formed, type points to a const (though the object it
// points to may or may not necessarily be const-qualified)
self_2.i = 5;
me_2->i = 5;
// always well-formed, always defined, nothing being modified
// (note: if the result/member was not an int and was a user-defined
// type, if it had its copy-constructor and/or operator= parameter
// as T& instead of const T&, like auto_ptr for example, this would
// be defined if the foo self_2/me_2 points to was non-const
int r = const_cast<foo&>(self_2).i;
r = const_cast<foo* const>(me_2)->i;
// always well-formed, always defined, nothing being modified.
// (same idea behind the non-const bar, only const qualifications
// are being changed, not any objects.)
const_cast<foo&>(self_2);
const_cast<foo* const>(me_2);
// always well-formed, defined if the foo pointed to is non-const
// (note, equivalent to using self and me)
const_cast<foo&>(self_2).i = 5;
const_cast<foo* const>(me_2)->i = 5;
// always well-formed, defined if the foo pointed to is non-const
const_cast<foo&>(*this).i = 5;
const_cast<foo* const>(this)->i = 5;
}
int i;
foo& self;
foo* me;
const foo& self_2;
const foo* me_2;
};
int main()
{
int i = 0;
{
// always well-formed, always defined
int& x = i;
int* y = &i;
const int& z = i;
const int* w = &i;
// always well-formed, always defined
// (note, same as using x and y)
const_cast<int&>(z) = 5;
const_cast<int*>(w) = 5;
}
const int j = 0;
{
// never well-formed, strips cv-qualifications without a cast
int& x = j;
int* y = &j;
// always well-formed, always defined
const int& z = i;
const int* w = &i;
// always well-formed, never defined
// (note, same as using x and y, but those were ill-formed)
const_cast<int&>(z) = 5;
const_cast<int*>(w) = 5;
}
foo x;
x.bar(); // calls non-const, well-formed, always defined
x.bar() = 5; // calls non-const, which calls const, removes const from
// result, and modifies which is defined because the object
// pointed to by the returned reference is non-const,
// because x is non-const.
x.baz(); // well-formed, always defined
const foo y;
y.bar(); // calls const, well-formed, always defined
const_cast<foo&>(y).bar(); // calls non-const, well-formed,
// always defined (nothing being modified)
const_cast<foo&>(y).bar() = 5; // calls non-const, which calls const,
// removes const from result, and
// modifies which is undefined because
// the object pointed to by the returned
// reference is const, because y is const.
y.baz(); // well-formed, always undefined
}
I refer to the ISO C++03 standard.
IMO, you are not doing anything technically wrong. May-be it would be simpler to understand if the member was a pointer.
class X
{
Y* m_ptr;
void foo() const {
m_ptr = NULL; //illegal
*m_ptr = 42; //legal
}
};
const makes the pointer const, not the pointee.
Consider the difference between:
const X* ptr;
X* const ptr; //this is what happens in const member functions
As to references, since they can't be reseated anyway, the const keyword on the method has no effect whatsoever on reference members.
In your example, I don't see any const objects, so you are not doing anything bad, just exploiting a strange loophole in the way const correctness works in C++.
Without actually getting to whether it is/should/could be allowed, I would greatly advice against it. There are mechanisms in the language for what you want to achieve that don't require writing obscure constructs that will most probably confuse other developers.
Look into the mutable keyword. That keyword can be used to declare members that can be modified within const member methods as they do not affect the perceivable state of the class. Consider class that gets initialized with a set of parameters and performs a complex expensive calculation that may not be needed always:
class ComplexProcessor
{
public:
void setInputs( int a, int b );
int getValue() const;
private:
int complexCalculation( int a, int b );
int result;
};
A possible implementation is adding the result value as a member and calculating it for each set:
void ComplexProcessor::setInputs( int a, int b ) {
result = complexCalculation( a, b );
}
But this means that the value is calculated in all sets, whether it is needed or not. If you think on the object as a black box, the interface just defines a method to set the parameters and a method to retrieve the calculated value. The instant when the calculation is performed does not really affect the perceived state of the object --as far as the value returned by the getter is correct. So we can modify the class to store the inputs (instead of the outputs) and calculate the result only when needed:
class ComplexProcessor2 {
public:
void setInputs( int a, int b ) {
a_ = a; b_ = b;
}
int getValue() const {
return complexCalculation( a_, b_ );
}
private:
int complexCalculation( int a, int b );
int a_,b_;
};
Semantically the second class and the first class are equivalent, but now we have avoided to perform the complex calculation if the value is not needed, so it is an advantage if the value is only requested in some cases. But at the same time it is a disadvantage if the value is requested many times for the same object: each time the complex calculation will be performed even if the inputs have not changed.
The solution is caching the result. For that we can the result to the class. When the result is requested, if we have already calculated it, we only need to retrieve it, while if we do not have the value we must calculate it. When the inputs change we invalidate the cache. This is when the mutable keyword comes in handy. It tells the compiler that the member is not part of the perceivable state and as such it can be modified within a constant method:
class ComplexProcessor3 {
public:
ComplexProcessor3() : cached_(false) {}
void setInputs( int a, int b ) {
a_ = a; b_ = b;
cached_ = false;
}
int getValue() const {
if ( !cached_ ) {
result_ = complexCalculation( a_, b_ );
cached_ = true;
}
return result_;
}
private:
int complexCalculation( int a, int b );
int a_,b_;
// This are not part of the perceivable state:
mutable int result_;
mutable bool cached_;
};
The third implementation is semantically equivalent to the two previous versions, but avoid having to recalculate the value if the result is already known --and cached.
The mutable keyword is needed in other places, like in multithreaded applications the mutex in classes are often marked as mutable. Locking and unlocking a mutex are mutating operations for the mutex: its state is clearly changing. Now, a getter method in an object that is shared among different threads does not modify the perceived state but must acquire and release the lock if the operation has to be thread safe:
template <typename T>
class SharedValue {
public:
void set( T v ) {
scoped_lock lock(mutex_);
value = v;
}
T get() const {
scoped_lock lock(mutex_);
return value;
}
private:
T value;
mutable mutex mutex_;
};
The getter operation is semantically constant, even if it needs to modify the mutex to ensure single threaded access to the value member.
The const keyword is only considered during compile time checks. C++ provides no facilities to protect your class against any memory access, which is what you are doing with your pointer/reference. Neither the compiler nor the runtime can know if your pointer points to an instance that you declared const somewhere.
EDIT:
Short example (might not compile):
// lets say foo has a member const int Foo::datalength() const {...}
// and a read only acces method const char data(int idx) const {...}
for (int i; i < foo.datalength(); ++i)
{
foo.questionable(); // this will most likely mess up foo.datalength !!
std::cout << foo.data(i); // HERE BE DRAGONS
}
In this case, the compiler might decide, ey, foo.datalength is const,
and the code inside the loop promised not to change foo, so I have to evaluate
datalength only once when I enter the loop. Yippie!
And if you try to debug this error, which will most likely only turn up if you compile with optimizations (not in the debug builds) you will drive yourself crazy.
Keep the promises! Or use mutable with your braincells on high alert!
You have reached circular dependencies. See FAQ 39.11 And yes, modifying const data is UB even if you have circumvented the compiler. Also, you are severely impairing the compiler's capacity to optimize if you don't keep your promises (read: violate const).
Why is Questionable const if you know that you will modify it via a call to its owner? Why does the owned object need to know about the owner? If you really really need to do that then mutable is the way to go. That is what it is there for -- logical constness (as opposed to strict bit level constness).
From my copy of the draft n3090:
9.3.2 The this pointer [class.this]
1 In the body of a non-static (9.3) member function, the keyword this is an rvalue a prvalue expression whose
value is the address of the object for which the function is called. The type of this in a member function
of a class X is X*. If the member function is declared const, the type of this is const X*, if the member
function is declared volatile, the type of this is volatile X*, and if the member function is declared
const volatile, the type of this is const volatile X*.
2 In a const member function, the object for which the function is called is accessed through a const access
path; therefore, a const member function shall not modify the object and its non-static data members.
[Note emphasis mine].
On UB:
7.1.6.1 The cv-qualifiers
3 A pointer or reference to a cv-qualified type need not actually
point or refer to a cv-qualified
object, but it is treated as if it
does; a const-qualified access path
cannot be used to modify an object
even if the object referenced is a
non-const object and can be modified
through some other access path. [
Note: cv-qualifiers are supported by
the type system so that they cannot be
subverted without casting (5.2.11).
—end note ]
4 Except that any class
member declared mutable (7.1.1) can be
modified, any attempt to modify a
const object during its lifetime (3.8)
results in undefined behavior.