Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it UB to change a member of a const object via a constructor-bound reference?

In short: Does the following code have Undefined Behavior, or is this fine?

struct X
{
  X(int b)
    : value(b)
    , ref(value)
  {
  }

  int value;
  int& ref;

  void isThisUB() const
  {
    ref = 1;
  }
};

int main()
{
  const X x(2);
  // Is either of these fine?
  x.isThisUB();
  x.ref = 3;
  return x.value;
}

https://godbolt.org/z/1TE9a7M4a

X::value is const for x. According to my understanding of const semantics, this means that modifying it in any way is UB. Yet we can take a non-const reference to it in the constructor and then modify it through that, either in a const member function or directly.

The C++ (at least 17) standard gives an example of const-related UB in [dcl.type.cv] that looks mostly the same, except it employs const_cast. Note how p->x.j = 99 is denoted as UB. I do not see a fundamental difference between achieving this with const_cast vs my above code.

So, is the code above UB? Are non-const reference members/pointers really this big of a footgun?

(If you can come up with search keywords that yield a related question and not just random const stuff, I'll be mighty impressed.)

like image 878
Max Langhof Avatar asked Nov 16 '21 10:11

Max Langhof


People also ask

Can you modify a const reference C++?

But const (int&) is a reference int& that is const , meaning that the reference itself cannot be modified.

Can a const reference be bound to a non-const object?

No. A reference is simply an alias for an existing object. const is enforced by the compiler; it simply checks that you don't attempt to modify the object through the reference r .

What is const object in c++?

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. A const member function can be called by any type of object.

What does it mean to create a const class member function and why would you want to do that?

A const member function is a member function that guarantees it will not modify the object or call any non-const member functions (as they may modify the object). Now getValue() has been made a const member function, which means we can call it on any const objects.


1 Answers

Does the following code have Undefined Behavior, or is this fine?

It has UB. Standard says:

[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.

x is const and you modify its non-mutable member.

I do not see a fundamental difference between achieving this with const_cast vs my above code.

Indeed. Both are UB for the same reason.

Are non-const reference members/pointers really this big of a footgun?

The trigger for the footgun is the issue that the object is temporarily non-const while it is within its constructor. Hence pointers and references to non-const "this" and its subobjects are readily available wthin the constructor regardless of whether the object is going to be const or not. Thus we can conclude that storing those pointers/references for later use is ill-advised.

Storing pointers and references as members referring to "this" are a footgun for several other reasons as well. They require storage that's otherwise unnecessary if you were to access the referred member through its name directly. Furthermore, you'll find that the copy-semantics of the class will likely not be what you had in mind.

If you want to point to a member out of several alternatives, then use a member-pointer, not an object pointer / reference (using storage cannot be avoided for such case). This solves both copying and accidental const violation.

like image 69
eerorika Avatar answered Oct 21 '22 16:10

eerorika