Consider the following example:
#include <new>
struct FunctionObject
{
int operator()() // non-const, modifies the state of this object
{
i += 1;
j += 2;
return i + j;
}
int i = 0;
int j = 0;
};
struct Wrapper
{
explicit Wrapper(const FunctionObject& input_object)
{
constructed_object = ::new (buffer) FunctionObject(input_object);
}
~Wrapper()
{
constructed_object->~FunctionObject();
}
int operator()() const // const, but invokes the non-const operator() of the internal FunctionObject
{
return (*constructed_object)(); // this call modifies the internal bytes of this Wrapper
}
alignas(FunctionObject) unsigned char buffer[sizeof(FunctionObject)];
FunctionObject* constructed_object = nullptr;
};
int test()
{
const FunctionObject function_object{3, 4};
const Wrapper object_wrapper{function_object}; // this call modifies the internal bytes of a const Wrapper
return object_wrapper();
}
A Wrapper contains an internal FunctionObject which is constructed inside the Wrapper by a placement new.
The Wrapper object is const, its operator() is also const, but calling it causes the internal state of the object to be modified. In many cases, similar scenarios are undefined behavior in C++.
The question is, is it undefined behavior in this particular case (~ do I need to mark the buffer as mutable?), or does the C++ standard allow writing code like this?
This is Undefined Behavior.
From [dcl.type.cv],
Any attempt to modify a const object during its lifetime results in undefined behavior.
Adding the mutable specifier to buffer will allow it to be modified by a const member function.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With