What is the difference between these two terms, and why do I need mutable?
What is the difference between these two terms, and why do I need mutable?
Some code for people who want to try out the effects of physical constness theirselfes:
#include <type_traits>
using namespace std;
using type = int const;
type &f();
int main()
{
const_cast<remove_const_t<type> &>(f()) = 123;
}
#if defined(__GNUC__) || defined(__llvm__)
__attribute__((noinline))
#elif defined(_MSC_VER)
__declspec(noinline)
#endif
type &f()
{
static type i = 0;
return i;
}
Under Linux and Windows this program crashes until you drop the const-specifier with type.
I respectfully disagree with Mike Seymour's answer:
A member function of a class can be defined as a const function, meaning that it will not modify any internal state of the object (except for members that are marked mutable, which becomes an important issue when we talk later about "logical" const-ness). That is the actual definition of "physical" const-ness. But even though a function is marked const, it may be logically modifying the objects state. Consider the following example:
#include <iostream>
using namespace std;
class Foo
{
private:
int *ip;
public:
Foo(int i) {
ip = new int(i);
}
virtual ~Foo() {
delete ip;
}
void modifyState(int i) const {
*ip = i;
}
int getI(void) const {
return *ip;
}
};
int main(int argc, char *argv[])
{
Foo foo(7);
cout << foo.getI() << endl;
foo.modifyState(9);
cout << foo.getI() << endl;
return 0;
}
Prints:
7
9
Member function modifyState does not modify member ip, so it satisfies the requirements for physical const-ness. But who can argue that since it is modifying what ip points to that the logical state has not been changed? So even though this function satisfies the requirements for physical cont-ness, it does not satisfy logical const-ness since what function getI will return has been changed by the call to modifyState.
Conversely, we can physically modify an object's member, but the state of the object as it appears to the outside world via its interface has not changed. Consider the following example where const functions getCelsiusTemperature and getFahrenheitTemperature modify mutable state but do not modify the object's logical state:
#include <iostream>
using namespace std;
class Temperature
{
private:
mutable float temperature;
mutable bool is_celsius;
public:
Temperature(double temperature, bool is_celsius=true) {
this->temperature = temperature;
this->is_celsius = is_celsius;
}
double getCelsiusTemperature(void) const {
if (!this->is_celsius) {
// convert to Celsius:
this->temperature = (this->temperature - 32.0) * 5.0 / 9.0;
this->is_celsius = true;
}
return this->temperature;
}
const double getFahrenheitTemperature(void) const {
if (this->is_celsius) {
// convert to Fahrenheit:
this->temperature = this->temperature * 9.0 / 5.0 + 32.0;
this->is_celsius = false;
}
return this->temperature;
}
};
int main(int argc, char *argv[])
{
const Temperature t(100, true);
cout << t.getCelsiusTemperature() << endl;
cout << t.getFahrenheitTemperature() << endl;
cout << t.getCelsiusTemperature() << endl;
return 0;
}
Prints:
100
212
100