Temporary mutability of a member variable - c++

Is it possible to have a member variable only be considered mutable for a given function/code block?
e.g.
class Foo() {
int blah;
void bar() const {
blah = 5; // compiler error
}
void mutable_bar() const {
blah = 5; // no compiler error
}
}
note: in this case I do NOT want to get rid of the const at mutable_bar since logical const will be preserved.
Same question, but different perspective: Can I somehow apply the mutable keyword to a method instead of a variable?

No, it is not possible, at least in C++. You need either mutable or non const function.
Also there is const_cast don't use it to modify things. In case if you modify const_casted const value you get Undefined Behaviour.
5.2.11 Const cast
7 [ Note: Depending on the type of the object, a write operation through the pointer, lvalue or pointer
to data member resulting from a const_cast that casts away a const-qualifier73 may produce undefined
behavior (7.1.6.1). —end note ]
7.1.6.1 The cv-qualifiers
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.
....
5 For another example
struct X {
mutable int i;
int j;
};
struct Y {
X x;
Y();
};
const Y y;
y.x.i++; // well-formed: mutable member can be modified
y.x.j++; // ill-formed: const-qualified member modified
Y* p = const_cast<Y*>(&y); // cast away const-ness of y
p->x.i = 99; // well-formed: mutable member can be modified
p->x.j = 99; // undefined: modifies a const member
—end example ]

It is technically possible to bypass the const in this case, by for example:
void mutable_bar() const {
int& p_blah = const_cast<int&>(blah);
p_blah = 5; // no compiler error
}
Or some similar construct. But you are really jumping through hoops to do something that you shouldn't be able to do. And as a comment on another post says, this is "undefined behavior", which means that in some cases it may not even work (or do what you expect it to do).

You could use a const_cast to make the member not-const in selected cases. This, along with an according comment, might even be a relatively clean solution. At least it's explicit that you break the const in a restricted scope, instead of making it world-wide mutable.

Related

Does const allow for (theoretical) optimization here?

Consider this snippet:
void foo(const int&);
int bar();
int test1()
{
int x = bar();
int y = x;
foo(x);
return x - y;
}
int test2()
{
const int x = bar();
const int y = x;
foo(x);
return x - y;
}
In my understanding of the standard, neither x nor y are allowed to be changed by foo in test2, whereas they could be changed by foo in test1 (with e.g. a const_cast to remove const from the const int& because the referenced objects aren't actually const in test1).
Now, neither gcc nor clang nor MSVC seem to optimize test2 to foo(bar()); return 0;, and I can understand that they do not want to waste optimization passes on an optimization that only rarely applies in practice.
But am I at least correct in my understanding of this situation, or am I missing some legal way for x to be modified in test2?
The standard says in [dcl.type.cv]:
Except that any class member declared mutable […] can be modified, any attempt to modify […] a const object […] during its lifetime […] results in undefined behavior.
It is also not possible to make this defined by ending the lifetime of the object prematurely, according to [basic.life]:
Creating a new object within the storage that a const complete object with […] automatic storage duration occupies, or within the storage that such a const object used to occupy before its lifetime ended, results in undefined behavior.
This means that the optimization of x - y to zero is valid because any attempt to modify x in foo would result in undefined behavior.
The interesting question is if there is a reason for not performing this optimization in existing compilers. Considering that the const object definition is local to test2 and the fact is used within the same function, usual exceptions such as support for symbol interposition do not apply here.

Is modifying non-mutable member of non-const object in const method Undefined Behaviour?

dcl.type.cv provides an interesting example:
For another example,
struct X {
mutable int i;
int j;
};
struct Y {
X x;
Y();
};
const Y y;
y.x.i++; // well-formed: mutable member can be modified
y.x.j++; // ill-formed: const-qualified member modified
Y* p = const_cast<Y*>(&y); // cast away const-ness of y
p->x.i = 99; // well-formed: mutable member can be modified
p->x.j = 99; // undefined: modifies a const member
which indicates that, via const_cast, one may modify mutable members of a const qualified object, while you can't do that with non-mutable members.
To my understanding, this is because of the original constness of y itself. What would happen if we got rid of the mutable keyword, the const qualifier fot y, but modified the fields in a const method?
Example below:
#include <vector>
struct foo {
std::vector<int> vec{};
void bar() const {
auto& raw_ref = const_cast<std::vector<int>&>(vec);
raw_ref.push_back(0); // ok?
auto* raw_this = const_cast<foo*>(this);
raw_this->vec.push_back(0); // ok?
}
};
int main() {
foo f{};
f.bar();
}
Does it exhibit Undefined Behaviour? I would think that it does not, since we're modifying an originally non-const, but in a const context.
Additionally, notice that I provided two ways of modifying the vec. One with non-const reference and one with non-const pointer to this (which was originally const in this constext due to foo::bar being a const method). Do they differ in any particular way, given the question's context? I would assume that both are okay here.
Disclaimer: I am aware of mutable keyword, but this desing (not only being flawed) is simply an example. One could assume that the author of the code wanted to prohibit every single way of modifying vec except for push_backs.
Your quoted paragraph actually spelled out exactly what is undefined [dcl.type.cv]
Except that any class member declared mutable can be modified, any attempt to modify a const object during its lifetime results in undefined behavior.
A const reference/pointer to a non-const object doesn't make that object a const object, all your accesses are well-formed.

mutable with const pointer in C++

If I use mutable with const pointer like this:
class Test
{
public:
mutable const int* ptr; // OK
};
It's working fine.
But, If I use like this :
class Test
{
public:
mutable int * const ptr; // Error
};
An error :
prog.cpp:6:25: error: const 'ptr' cannot be declared 'mutable'
mutable int * const ptr;
^
prog.cpp: In function 'int main()':
prog.cpp:11:7: error: use of deleted function 'Test::Test()'
Test t;
^
prog.cpp:3:7: note: 'Test::Test()' is implicitly deleted because the default definition would be ill-formed:
class Test
^
prog.cpp:3:7: error: uninitialized const member in 'class Test'
prog.cpp:6:25: note: 'int* const Test::ptr' should be initialized
mutable int * const ptr;
Why does compiler give an error in second case?
const int * ptr;
The first is a pointer to constant data, which means you can change the pointer and where it points to, but you can't change the data it points to.
int * const ptr;
The second is a constant-pointer to non-constant data, which means you must initialize the pointer in your constructor(s) and then you can't make it point anywhere else. The data it points to can be modified though.
The mutable part in both cases applies to the pointer, the actual member variable, not the data it points to. And since the variable can't be both mutable and constant at the same time you should get an error message for that.
The 2nd case causes error because mutable and const can't be mixed; mutable can only be used with non-const data member.
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.
BTW the following code causes the same error.
class Test
{
public:
mutable const int x; // Error;
mutable int const x; // ditto;
};
The 1st case is fine, because const int* is not a const pointer, but a pointer to const. That means it's fine to modify the pointer itself, and you can mark it mutable. (But you can't modify the pointee.)
BTW const pointer to const (e.g. mutable const int* const ptr;) causes the same error too.
struct Test
{
const int* ptr;
};
Translation: "The structure has a member. The member is a pointer. The pointer points to an integer which may not be mutated via the pointer."
The pointer itself may be mutated though, to point to a different const int.
It's probably simpler if we pick a non-reference type, so you can separate the types of the member (in your example a pointer), from the type of the pointed-to object.
struct Test1
{
int value;
};
Now, adding the mutable keyword to get
struct Test2
{
mutable int value;
};
just means we're allowed to mutate the member even when the structure itself is otherwise const.
In other words, all of this is OK in both cases:
Test1 a { 123 };
Test2 b { 123 };
// now mutate the object through a non-const reference
a.value = 42;
b.value = 42;
but this is different:
const Test1& ca = a;
ca.value = 69; // NO, a member of a const object is const
const Test2& cb = b;
cb.value = 69; // OK, a mutable member of a const object
So, now that we understand how mutable is being applied, the consider the problematic line:
mutable int * const ptr;
This is saying that ptr is both mutable (able to be mutated even when the object it's a member of is otherwise const) and const (not able to be mutated even when the object it's a member of is otherwise non-const).
The two are obviously contradictory.
The bugs.eclipse.org say's:
The mutable specifier can be applied only to names of class data
members (9.2) and cannot be applied to names declared const or
static, and cannot be applied to reference members.

Why it does not declare a reference type if 'auto' var is initialized using a function returning reference?

When an 'auto' var is initialized using a function that returns a reference, why the var type is not a reference?
e.g. In following example, why type of x is Foo and not Foo& ?
class TestClass {
public:
Foo& GetFoo() { return mFoo; }
private:
Foo mFoo;
};
int main()
{
TestClass testClass;
auto x = testClass.GetFoo(); // Why type of x is 'Foo' and not 'Foo&' ?
return 0;
}
EDIT: The link explains how to get the reference, but my question is the reason for this behavior.
Because it would be annoying if it worked that way. How, for example, would you specify that you didn't want a reference?
When you use auto, you need to put const, &, &&, and volatile in yourself.
auto& x = testClass.GetFoo();
is your fix.
C++11 auto type inference rules drop reference, const and volatile qualifiers.
However, you may ask C++ compiler to use decltype type inference rules to keep all these qualifiers for declaring variable type. In your case it may be:
decltype(auto) x = testClass.GetFoo();
But this code can cause some side effects like reference to destroyed object, so you need to keep in mind the real variable type and life time.

Difference between const int &x = 4 and const int x = 4

The sole purpose of references is aliasing. Assigning a reference (referring to a constant int) to a integer seems absurd since it is not an alias (and it doesn't give an error!). I suppose it is similar to defining a constant int itself. Is there any difference?
Within a function body or file scope, the only difference is decltype(x). In one case, it is int const and the other int const&.
The const int & x=7; creates a temporary anonymous int with value 7. It then binds a reference x to it. The lifetime of the temporary is the extended to that of the reference. This is basically indistinguishable from x being the name of a const int with value 7.
An exception to it being nigh identical is when the binding occurs within an object's constructor as part of member initialization. In that case, the lifetime is not extended.
I suspect you can induce this with:
struct Foo{
int const& x=7;
Foo(){};
};
Either the above syntax is illegal or it dangles (I do not recall if there is a corner case in the standard for references), while:
struct Foo{
int const x=7;
Foo(){};
};
is both legal and does not dangle. So there is a difference.
There would also be a difference as a parameter to a function, wbhere =7 simply provides a default.