4

Example:

class A {
  const int &x;

 public:
  A(const int &x_) : x{x_} {}

  void foo() {
    std::cout << x << std::endl;
  }
}

int main() {
  int x{0};
  A a{x};
  a.foo() // prints "0"
  x = 1;
  a.foo() // prints "0" or "1"??
}

Can I safely assume that A::foo() will always read the correct value or is something like a volatile modifier needed?

Hex
  • 85
  • 4
  • of course yes. because you save pointer to variable in class. – RbMm Dec 16 '19 at 02:09
  • 2
    Will print 1. The const in the class says the class will not change the value; it does not mean someone else won't. – ChrisMM Dec 16 '19 at 02:10
  • 1) are you talking a multithreaded environment, or single thread? 2) Volatile most likely doesn't do what you think it does in C++. Adding it here wouldn't necessarily make your variable safer to read. –  Dec 16 '19 at 04:52
  • 1
    Unrelated to the question asked, but make sure you understand, that this design leaks implementation details into client code: The fate of `A` instances is bound to the lifetime of the object passed into the constructor. This is hard to establish and verify. For example, the following will break: `A make_a() { int x{42}; return A{x}; }`. – IInspectable Dec 21 '19 at 18:08

3 Answers3

3

Is it safe to use a reference to a const when the referenced variable changes from outside?

Yes.

Can I safely assume that A::foo() will always read the correct value

Yes... unless you assume the value to be other than what the correct one is. 1 would be the correct value in the latter function call.

eerorika
  • 232,697
  • 12
  • 197
  • 326
0

In a singly-threaded program, like the one you have shown, this is perfectly safe. Your first call to foo::A() will execute in its entirety before x = 1, which executed before the next call to foo::A(). The second call is always guaranteed to read 1, unless you have some undefined behavior somewhere else in your program.

In a multi-threaded program, the const only requires that foo not change the value of x, not that other threads don't change its value. Thus, something like this might require synchronization to read this properly. An std::mutex and/or std::atomic might help you with that part of it.

As to using volatile, volatile isn't really for what you seem to think it is for. It is generally for cases when the variable can be changed outside the context of the compiler. Using volatile here won't necessarily make your read safer. Use standard synchronization methods instead to ensure your read is safe.


Oh, and by the way, to answer this question:

// prints "0" or "1"??

This prints 1. You passed it by reference, so changing the value in main() will change the value stored in foo. That's one of the advantages, if you will, of passing by reference.

  • 1
    Volatile semantics imply that the compiler can never assume that a read or write is redundant, which is often what people expect when dealing with variables shared by multiple threads. But there are other things you need that volatile doesn't provide, and that atomics guarantee, so... don't use volatile. – curiousguy Jan 03 '20 at 19:58
  • Agreed. That's what I was trying to say, but I don't know how clear i was. –  Jan 03 '20 at 20:00
0

There are very few uses of volatile; most (not all though) relate to object state changing asynchronously or in ways unknowable by the compiler.

Calling a function in your own code is not an unknowable flow. It's very well known that a called function can change the values of any object that can be designated by the function (with or without using the object name) and whose value can legally be changed (that isn't const).

curiousguy
  • 8,038
  • 2
  • 40
  • 58