I just discovered how easy it is to modify const objects without any const_cast
black magic. Consider:
#include <iostream>
class Test {
public:
Test(int v)
:m_val{ v },
m_ptr{ &m_val }
{}
int get() const { return m_val; }
void set(int v) const { *m_ptr = v; }
private:
int m_val;
int* m_ptr;
};
int main()
{
const Test t{ 10 };
std::cout << t.get() << '\n';
t.set(0);
std::cout << t.get() << '\n';
return 0;
}
Recent versions of Clang, GCC, and MSVC don't show any warning and produce expected output:
10 0
Is this well defined behavior according to the current standard? If it's undefined what if m_val
was of type std::aligned_storage_t<sizeof(int), alignof(int)>
and constructor new
'ed int
in it? I believe it's pretty common case when it comes to small buffer optimizations.
Edit
Thanks, it seems that it's just another way to shoot yourself in a foot. What's troubling it seems that this:
struct Test2 {
int i;
void operator()() { ++i; }
};
const std::function<void()> f{ Test2{ 10 } };
f();
is also undefined behavior when implementation chooses to store the Test2
object inside f
(and that's the case in libc++ and in Visual Studio)
const int * is a pointer to an integer constant. That means, the integer value that it is pointing at cannot be changed using that pointer.
A pointer to constant is a pointer through which the value of the variable that the pointer points cannot be changed. The address of these pointers can be changed, but the value of the variable that the pointer points cannot be changed.
Because the data type being pointed to is const, the value being pointed to can't be changed. We can also make a pointer itself constant. A const pointer is a pointer whose address can not be changed after initialization.
Const member functions in C++ C++ProgrammingServer Side Programming. The const member functions are the functions which are declared as constant in the program. The object called by these functions cannot be modified. It is recommended to use const keyword so that accidental changes to object are avoided.
const
enforces "bitwise constness", but what you usually want is "logical constness".
In the case of an object that contains a pointer, this means that a const member function can't modify the pointer itself, but can modify what the pointer refers to. In other words, these examples are well formed, but have undefined behavior.
To get logical constness, you 1) use mutable
(or sometimes const_cast
) to allow modification of members that don't affect the object's logical state (e.g., cached values/memoization), and 2) generally have to manually enforce not writing to data through a pointer (but if it's an owning pointer, that ownership should probably be delegated to an object that only manages ownership of that data, in which case making it const should normally prevent writing to the data it owns).
As far as the specific detail of having a non-const pointer pointing to data that might itself have been const modified, well, you're basically just getting a (persistent) version of roughly the same thing that const_cast
is typically used to do: get non-const access to data to which you'd otherwise only have a const
pointer. It's up to you to ensure that you only use this in ways that doesn't cause a problem (but just having and/or writing through that pointer doesn't, in itself, necessarily lead to a problem).
In other words, what we have here are two separate pointers to some data. this
lets you access an object's data. In a const
member function, you can only read (not) write data via this
, unless (as noted above) it's marked mutable
. In this case, you're saving a second pointer to the same data. Since there's nothing to mark that as a pointer to const
, it's not, so you get non-const access to the data it points at.
As others pointed out in comments: you are modifying object the m_ptr
points to. This "pointed to" object is not a part of class Test
(as far as compiler sees it). That's why compiler allows you to do so.
Having said that, I believe that it will be undefined behaviour. That's because m_ptr
actually points to another member variable (m_val
) of object const Test t
! Compilers are allowed to optimize arggresively and they might rely on constness to do so.
The only exception is then you use mutable
keyword, but it's another story.
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