<p>C++ does not generate default copy/move constructors or assignment operators for any <code>volatile</code> <code>struct</code> or <code>class</code> type. That is, given these three declarations, all of the following statements are illegal:</p>
<pre class="lang-cpp prettyprint-override"><code>class Foo {}; // or struct Foo {};
Foo a;
volatile Foo b;

// ALL of these are illegal:
a = b;
b = a;
Foo c = b;
Foo d = std::move(b);
volatile Foo e = b;
b = b; // a=a is useless but allowed. b=b is not permitted.
</code></pre>
<p>Furthermore, if you define constructors and <code>operator=</code>s to allow these, then all the default ones go away, and you have to explicitly default them. Defining all this is a massive amount of boilerplate even for a small PoD class:</p>
<pre class="lang-cpp prettyprint-override"><code>class Bar {
  int m_a, m_b;
public:
  constexpr Bar(int a, int b) noexcept : m_a(a), m_b(b) {}
  // Everything below this line is the aforementioned boilerplate
  Bar(const Bar&amp;) = default;
  Bar(Bar&amp;&amp;) = default;
  Bar&amp; operator=(const Bar&amp;) = default;
  Bar&amp; operator=(Bar&amp;&amp;) = default;
  Bar(const volatile Bar&amp; other) noexcept : m_a(other.m_a), m_b(other.m_b) {}
  Bar(volatile Bar&amp;&amp; other) noexcept : m_a(std::move(other.m_a)), m_b(std::move(other.m_b)) {}
  Bar&amp; operator=(const volatile Bar&amp; other) noexcept {
    m_a = other.m_a;
    m_b = other.m_b;
    return *this;
  }
  volatile Bar&amp; operator=(const volatile Bar&amp; other) volatile noexcept {
    m_a = other.m_a;
    m_b = other.m_b;
    return *this;
  }
  Bar&amp; operator=(volatile Bar&amp;&amp; other) noexcept {
    m_a = std::move(other.m_a);
    m_b = std::move(other.m_b);
    return *this;
  }
  volatile Bar&amp; operator=(volatile Bar&amp;&amp; other) volatile noexcept {
    m_a = std::move(other.m_a);
    m_b = std::move(other.m_b);
    return *this;
  }
};
</code></pre>
<p>Why does C++ not generate these constructors and assignment operators for volatile types? Were they just considered unnecessary, or is there some technical reason they shouldn't exist by default?</p>

<c++><assignment>
Why are volatile objects so difficult to work with in C++?
CC BY-SA 4.0
2023-12-03T12:51:05.967Z
Often unnoticed: The C language guarantees that volatile objects that have been modified between setjmp() and longjmp() are not restored. Usually longjmp() will restore all registers and volatile keeps things out of registers so this will happen by coincidence, but the language actually gives a guarantee.
gnasher729
CC BY-SA 4.0
2023-12-16T05:41:35.583Z
related cppcon talk: [Deprecating volatile - JF Bastien - CppCon 2019](https://youtu.be/KJW_DLaVXIY)
starball
<p>Although this was well explained in the previous answers, I like when you can demonstrate it with a real-life example.</p>
<p>I admit I never saw usage of the <code>volatile</code> qualifier, except in the embedded software world for primitive types, i.e., pointers to memory locations.
And I don’t know what purpose it would serve in the C++ object world.</p>
<p>Compilers consider memory access &quot;expensive&quot; and try to avoid it whenever they can, by storing its content in CPU registers.</p>
<p>The purpose of <code>volatile</code> is just to tell compiler not to make any assumptions and optimizations regarding handling variables and memory access.</p>
<p>Basically, it must assume that the content of a memory location accessed by a pointer
can change independently of our code, at any time (asynchronously by hardware, in the <a href="https://en.wikipedia.org/wiki/Interrupt_handler" rel="nofollow noreferrer">ISR</a>).</p>
<p>Let’s see those examples.
Here code reads some peripheral status register and checks when the content of a register changes to 0x02:</p>
<pre><code>uint8_t * stat_reg = (uint8_t *) 0x08001234;
// Wait for register to become non-zero
do { ... } while (0 == *stat_reg)
</code></pre>
<p>The compiler doing optimization could do something like this.
Having already read the register content from the peripheral register at 0x1234,
the compiler figures there isn't any need reading from this memory location again, so the compiler places the content of memory in the CPU accumulator
and checks the accumulator instead:</p>
<pre><code>mov stat_reg, #0x08001234
mov a, @stat_reg
loop:
   ...
   bz loop
</code></pre>
<p>Needless to say why this assembly code would never work.</p>
<p>So <code>volatile</code> is used here to tell the compiler not to make any optimizations when accessing the address the pointer points to.</p>
<pre><code>uint8_t volatile * stat_reg = (uint8_t volatile *) 0x08001234
</code></pre>
<p>Let’s try it with a real example with the help of <a href="https://en.wikipedia.org/wiki/GNU_Debugger" rel="nofollow noreferrer">GDB</a>:</p>
<pre><code>#include &lt;stdio.h&gt;

int func1(volatile int* i) {
    int a = 31;

    while (*i == 0)
        a++;

    return a;
}

volatile in reg = 0;

int main() {
    return reg + func1(&amp;reg);
}
</code></pre>
<p>If we disassemble our snippet where variable <em>reg</em> represents our hardware register:</p>
<pre><code>   0x000055555555512d &lt;+4&gt;:     mov    (%rdi),%eax
   0x000055555555512f &lt;+6&gt;:     test   %eax,%eax
   0x0000555555555131 &lt;+8&gt;:     jne    0x555555555142 &lt;func1+25&gt;
   0x0000555555555133 &lt;+10&gt;:    mov    <span class="math-container">$0x1f,%eax
   0x0000555555555138 &lt;+15&gt;:    add    $</span>0x1,%eax
=&gt; 0x000055555555513b &lt;+18&gt;:    mov    (%rdi),%edx
   0x000055555555513d &lt;+20&gt;:    test   %edx,%edx
   0x000055555555513f &lt;+22&gt;:    je     0x555555555138 &lt;func1+15&gt;
</code></pre>
<p>We see that in each iteration value of volatile register, (which address is in $rdi) will be loaded from memory, <code>mov (%rdi),%edx</code>, and tested, <code>test %edx,%edx</code>.</p>
<p>Now if we recompile it without the <code>volatile</code> qualifier, the compiler assumes that the value of <code>reg</code> never changes, and there isn't any need for reading from memory in each iteration, so the <code>while</code> loop condition is always true.
We end with an infinite loop in optimized code:</p>
<pre><code>   0x000055555555512d &lt;+4&gt;:     cmpl   <span class="math-container">$0x0,(%rdi)
   0x0000555555555130 &lt;+7&gt;:     jne    0x555555555134 &lt;func1+11&gt;
=&gt; 0x0000555555555132 &lt;+9&gt;:     jmp    0x555555555132 &lt;func1+9&gt;
   0x0000555555555134 &lt;+11&gt;:    mov    $</span>0x1f,%eax
</code></pre>

CC BY-SA 4.0
2023-12-02T10:07:34.997Z
2023-12-03T13:19:08.483Z
2023-12-03T13:19:08.483Z
Dražen Grašovec
CC BY-SA 4.0
2023-12-02T12:28:35.130Z
I mentioned this in the comments on another answer, but I’m using the volatile objects to communicate between interrupt handlers and the main function. The interrupt handlers check certain sensors and then write the sensor data to a global variable, and unless that variable is `volatile` the main function will never see the sensor data.
Bbrk24
CC BY-SA 4.0
2023-12-02T18:08:25.967Z
Welcome. I am confused as to why you posted a screenshot of the code as opposed to the text of the code. Besides that, this is a great first answer.
CPlus
CC BY-SA 4.0
2023-12-02T18:25:53.237Z
Sorry, is there some other reason against posting screenshot, or just because you cannot copy example?
Dražen Grašovec
CC BY-SA 4.0
2023-12-02T19:58:15.063Z
@DrazenGrasovec Posting a screenshot requires more data for others to load and isn't as easy to read for visually impaired users.
Bbrk24
CC BY-SA 4.0
2023-12-02T22:22:55.913Z
Accessibility is the main reason not to post text/code as images, as Bbrk mentioned. As well as the fact that some people can't access Imgur/have images disabled.
rydwolf
CC BY-SA 4.0
2023-12-03T12:43:26.070Z
OK. I admit didn't considered that. I will keep to  that rule in future
Dražen Grašovec
CC BY-SA 4.0
2023-12-04T02:31:53.773Z
@Bbrk24 I think there's a lot here that's superfluous to your actual question.  My answer is essentially the *only* legal thing an ISR can do in C.  `volatile' here tells the compiler that it must actually read the variable (all reads and writes are treated as sequenced side-effects), and can't re-order it at compile time in regards to other sequenced side-effects. (Conforming programs before C++11 were single-threaded).

Since C++11 and a C++ thread model, strictly conforming programs can use std::atomic and std::atomic_signal_fence as well, which is appropriate for multithreaded use.
DanielN
<h1>What <code>volatile</code> actually means</h1>
<p>The meaning of <code>volatile</code> in C and C++ is often poorly understood. The main use case for <code>volatile</code> is low-level programming on embedded systems that utilize <a href="https://en.wikipedia.org/wiki/Memory-mapped_I/O_and_port-mapped_I/O" rel="noreferrer">memory-mapped I/O</a>. In these systems, portions of the address space are <em>not</em> mapped to ROM or RAM; instead, reads from or writes to these special addresses are arbitrarily interpreted as commands by other hardware attached to the bus. In these cases, the usual properties of memory do not apply:</p>
<ul>
<li><p>When an address refers to memory, merely <em>reading</em> the value at that address has no side effects, so unused reads can be deleted by the compiler without changing program semantics. However, when an address refers to a memory-mapped I/O device, a “read” can actually modify the device’s state!</p>
<p>As a very simple hypothetical example of this, an address might be mapped to a <a href="https://en.wikipedia.org/wiki/Shift_register" rel="noreferrer">shift register</a>. Each time the address is read, one value might be shifted out, so repeated reads will return successive values in the shift register. “Unused” reads still advance the register, so removing them changes the program’s semantics.</p>
</li>
<li><p>Similar to the above, when an address refers to memory, reading from it multiple times with no intervening writes or synchronizations can always be assumed to return the same value. As the shift register example above illustrates, this is not necessarily true.</p>
</li>
<li><p>When an address refers to memory, writing to an address multiple times with no intervening reads or synchronizations can always be safely coalesced to a single write (of the final value). If the address refers to a memory-mapped I/O device, each store can have side effects, so this is not safe in general.</p>
</li>
<li><p>When an address refers to RAM, a write followed immediately by a read of the same memory address can always be assumed to return the written value. If the address refers to a memory-mapped I/O device, reads and writes to the same address can be interpreted in completely different ways, so there is no requirement that this be true.</p>
</li>
</ul>
<p>In other words, reads from or writes to an address mapped to an I/O device is really more like a kind of hardware procedure call than a traditional load or store from memory. This is incompatible with the standard memory semantics of the C and C++ abstract machines, which assume that memory addresses refer to real memory and therefore behave in the usual way.</p>
<p><code>volatile</code> is used to instruct a C or C++ compiler that a variable contains an address that is not mapped to conventional memory. This suppresses most of the compiler’s memory-related optimizations: loads and stores through <code>volatile</code> references are never dropped, coalesced, duplicated, or reordered. In other words, loads and stores through <code>volatile</code> addresses are optimized in much the same way as a call to an unknown function.</p>
<h2><code>volatile</code> is not useful for multithreading</h2>
<p>There is a widespread (false) belief that <code>volatile</code> is useful in multithreaded programs to ensure mutations are consistently visible across threads, but this is not the case. <code>volatile</code> only requires that the compiler emit a load or store instruction for each load or store in the source program, but this is not sufficient to ensure consistent visibility across threads on hardware that uses a <a href="https://en.wikipedia.org/wiki/Memory_ordering" rel="noreferrer">weak memory model</a>. On such systems, speculative execution can reorder non-conflicting loads and stores to conventional memory, so even if <code>volatile</code> is used, <a href="https://en.wikipedia.org/wiki/Memory_barrier" rel="noreferrer">memory barriers</a> are still necessary to ensure consistent ordering of mutations between threads.</p>
<p>(In theory, a C or C++ compiler <em>could</em> interpret <code>volatile</code> differently and automatically insert memory barriers to make it useful in this way. However, this is not guaranteed by the standard for either language, and GCC and Clang do not interpret it this way. By default, MSVC <em>does</em> interpret it this way on x86 and x86_64, but it <a href="https://learn.microsoft.com/en-us/cpp/build/reference/volatile-volatile-keyword-interpretation?view=msvc-170" rel="noreferrer">does not on ARM</a>, and it inhibits useful optimizations that more targeted synchronization constructs do not, so relying on this behavior is not recommended.)</p>
<h2><code>volatile</code> being a qualifier is somewhat strange</h2>
<p>The fact that <code>volatile</code> is a qualifier like <code>const</code> is a somewhat strange decision because it is not really a property of the variable. Instead, <code>volatile</code> essentially changes the meaning of the <code>*</code> and <code>-&gt;</code> operators when used on that variable. Instead of providing the <code>volatile</code> keyword, C could have simply provided volatile reads and writes as separate operations. For example, instead of writing</p>
<pre class="lang-c prettyprint-override"><code>volatile unsigned char* io_addr = 0xdeadbeef;
*io_addr = 0x01;
return *io_addr;
</code></pre>
<p>one could write</p>
<pre class="lang-c prettyprint-override"><code>mio_store(0xdeadbeef, 0x01);
return mio_load(0xdeadbeef);
</code></pre>
<p>which would arguably make it much clearer that these are not ordinary reads and writes but more like procedure calls. Indeed, this is precisely the approach that Rust takes: it provides the <a href="https://doc.rust-lang.org/std/ptr/fn.read_volatile.html" rel="noreferrer"><code>std::ptr::read_volatile</code></a> and <a href="https://doc.rust-lang.org/std/ptr/fn.write_volatile.html" rel="noreferrer"><code>std::ptr::write_volatile</code></a> functions instead of providing a special qualifier.</p>
<p>C’s decision to treat <code>volatile</code> as a qualifier makes more sense when considered from a historical perspective. C’s original purpose was essentially to be a fancy portable macro assembler, and C programs were originally written by assembly programmers who expected to be able to predict the assembly that C compilers would generate. From this point of view, it makes sense to want a guarantee that loads and stores to certain variables really produce loads and stores, and this naturally led to the <code>volatile</code> keyword. Modern C and C++ compilers diverged from this original philosophy decades ago, so the design of <code>volatile</code> has unfortunately outlived its original usefulness.</p>
<h1><code>volatile</code> objects and C++ classes</h1>
<p>All of the above describes the meaning of <code>volatile</code> when used on references to primitive types, like <code>char*</code> or <code>int*</code>, but C++ substantially complicates the meaning of <code>const</code> and <code>volatile</code> qualifiers, also known as <em>cv-qualifiers</em>. When <code>volatile</code> is applied to a variable of composite type like a <code>struct</code> or <code>class</code>, then references to members through that variable are considered volatile as well. Additionally, <code>const</code> and <code>volatile</code> can also be used to qualify <em>methods</em>, and if a method is invoked through a cv-qualified object, the method must be appropriately cv-qualified.</p>
<p>Given the meaning of <code>volatile</code>, this restriction makes sense. Keep in mind that <em>every</em> <code>volatile</code> load and store should be treated like a side-effectful procedure call, so order very much matters! This substantially complicates the meaning of copy constructors and <code>operator=</code>, each of which perform several sequential reads and writes to the objects’ members. Changing the order in which these assignments are performed could very well result in completely different behavior due to different ordering of effects, so it is likely better that the programmer specify that order explicitly.</p>
<p>All that said, by this point you should hopefully be wondering whether a <code>volatile</code>-qualified composite object is terribly useful in the first place. Given that <code>volatile</code> is generally useful with memory-mapped I/O, the addresses targeted by volatile loads and stores are generally fixed and statically-known. It makes little sense to copy an object from a volatile reference because the resource identified by a memory-mapped I/O address is a piece of actual hardware that cannot simply be duplicated. In most cases, <code>volatile</code> should not be used at all, and when it is, it should be used on values of primitive type, not objects.</p>

CC BY-SA 4.0
2023-12-01T00:39:31.567Z
2023-12-01T00:39:31.567Z
Alexis King
CC BY-SA 4.0
2023-12-01T03:42:58.927Z
"volatile is not useful for multithreading" -- Although this caveat almost certainly isn't relevant to OP's question, I do like to qualify this with "not useful at the application level". It can sometimes have a place deep in the guts of the _implementation_ of concurrency primitives. (And one can also use it correctly for sharing state with signal handlers, which are not exactly concurrency, but are not mmio either. However, now that atomics exist, one can and should use those instead.)
Glenn Willen
CC BY-SA 4.0
2023-12-01T07:59:24.737Z
@GlennWillen In a multithreaded context, it is truly never useful: even if atomics cannot be used (which is rare), making `volatile` useful would still require the use of memory barriers, but the ordering constraints imposed by `atomic_thread_fence` apply to non-`volatile` references (for better or for worse), so the `volatile` qualifier serves no purpose. I concede that it is theoretically useful for state used by signal handlers, though as you say, this seems more academic than practical.
Alexis King
CC BY-SA 4.0
2023-12-01T08:12:10.447Z
Hmm, I will have to weaken my claim to "I'm pretty sure once upon a time it was useful in this way in C, but perhaps they have standardized enough useful primitives to make it moot." Very good if they have. (I see that atomic_thread_fence is in C++11, and frankly I learned all the scary things I know about concurrency in C before 2011, and have mostly summarized them at this point to "don't.")
Glenn Willen
CC BY-SA 4.0
2023-12-01T12:16:35.683Z
"`volatile` is not useful for multithreading" – It probably doesn't help that in Java, a language with superficially C-like syntax, that is *exactly* what `volatile` is most useful for. So, when programmers come out of a "Java school" and later learn C or C++, they have this meaning engrained in their brains.
Jörg W Mittag
CC BY-SA 4.0
2023-12-01T12:24:21.610Z
Nitpick: A `volatile struct` can be useful to concretely define the layout of a MMIO region that has multiple related components to it such that you can refer to that region with a single variable or reuse the layout for multiple regions if you have multiple devices with the same layout. This can make code utilizing that MMIO region easier to read, because it groups all the bits together as one logical unit. I agree though that a `volatile class` in C++ is essentially never useful.
Austin Hemmelgarn
CC BY-SA 4.0
2023-12-01T15:13:32.533Z
For context, my use case is sending data between interrupt handlers and the main function. Without `volatile`, the main function won't see any changes from the interrupt handlers.
Bbrk24
CC BY-SA 4.0
2023-12-01T22:14:01.120Z
@Bbrk24, sounds like you should be using multithreading synchronization constructs instead.
Mark
CC BY-SA 4.0
2023-12-01T23:02:45.923Z
@GlennWillen: The Linux kernel still uses `volatile` to roll its own lock-free atomics on the few compilers it supports (GCC and I think Clang which make an effort to do `volatile` accesses with a single load or store for types of register width or narrower, unlike with non-volatile types: [Which types on a 64-bit computer are naturally atomic in gnu C and gnu C++? -- meaning they have atomic reads, and atomic writes](https://stackoverflow.com/q/71866535)).
Peter Cordes
CC BY-SA 4.0
2023-12-01T23:06:51.943Z
@GlennWillen: Without barriers (e.g. the Linux kernel's functions that use inline asm), `volatile` is like load or store with `memory_order_relaxed`, except that only run-time reordering of volatile ops with other volatile ops is allowed, not compile-time.  Visibility between threads/cores *does* happen on all mainstream CPUs because they have coherent cache between the cores that run threads of a program or OS; see [my answer about `volatile` and the low level details](//stackoverflow.com/a/58535118). (Communication between non-cache-coherent cores like an MCU + DSP needs a manual flush.)
Peter Cordes
CC BY-SA 4.0
2023-12-01T23:09:53.717Z
(Often you want at least [acquire / release semantics](https://preshing.com/20120913/acquire-and-release-semantics/) for communicating between threads; that's what this answer is talking about with the phrasing "**consistent** visibility", with consistency referring to ordering of operations on different memory locations, as observed by other threads.  C++ `std::memory_order_relaxed` doesn't give you that either, and normally compiles to the same instructions as for `volatile`, except of course for atomic RMWs like `fetch_add` or `compare_exchange_weak`, which `volatile` doesn't use.)
Peter Cordes
CC BY-SA 4.0
2023-12-02T10:50:14.133Z
Whether it is not the best practice, I remember that sharing global variable between threads of execution was stated as a case where volatile should be used. From the C/C++ manual:

3.4.2 Use the Volatile Keyword for Necessary Memory Accesses

The compiler analyzes data flow to avoid memory accesses whenever possible. If you have code that

depends on memory accesses exactly as written in the C/C++ code, you must use the volatile keyword to

identify these accesses. The compiler does not optimize out any references to volatile variables.
Dražen Grašovec
CC BY-SA 4.0
2023-12-02T12:02:16.540Z
I think the optimizer is allowed to transform `while (atomic_bool.load());` into `if (atomic_bool.load()) while(true);`, so sometimes atomics alone may not directly do what one wants. (Not sure how to actually do that properly in C++ at the moment, I just remember that I once threw in an additional volatile to a load in llvm IR.)
Carsten S
CC BY-SA 4.0
2023-12-02T21:23:33.297Z
Thanks for this explanation, especially  about MSVC behavior on x86. I've seen the claim the volatile isn't useful for ensuring that writes are visible across threads many times but had never made the connection that this is a particular feature of the x86 (the only CPU with which I am at all familiar) rather than simply how volatile works. It may also not help that when the texts for C and C++ were first written multi-core systems really were not of concern to the typical programmer. Those texts basically said "volatile is a pessimization ensuring that all reads and writes actually occur".
SoronelHaetir
CC BY-SA 4.0
2023-12-03T18:41:16.123Z
The benefit of `volatile` being in the type system is that the type system prevents you from accidentally attempting an ordinary access that should be a volatile access. Perhaps Rust, with its borrow checker, has some other way of preventing this; I wouldn't know.
Brian Bi
<p>In a follow on comment, OP mentioned that his use case is on ISRs.  Because an ISR is so tightly constrained on what it can do, and the nature of interrupts guarantees their memory ordering, the following is one of the valid uses of <code>volatile</code> in the context of this question:</p>
<pre><code>volatile sig_atomic_t isrFlag{0};

void interrupt_handler() { isrFlag = 1; }

int main() {
    for (;;) {
       if (isrFlag) {
           // Do things
       }
    }
}
</code></pre>
<p>There is no need for an additional memory fence here.
<a href="https://en.cppreference.com/w/c/program/sig_atomic_t" rel="noreferrer">https://en.cppreference.com/w/c/program/sig_atomic_t</a></p>

CC BY-SA 4.0
2023-12-01T22:27:52.353Z
2023-12-01T22:27:52.353Z
DanielN
CC BY-SA 4.0
2024-07-22T17:17:45.947Z
Note that even on a single processor system, clang and gcc would require using a memory fence or memory clobber directive to ensure that ordinary accesses which occur before a write to a `volatile sig_atomic_t` don't get reordered across it.
supercat
# Why are volatile objects so difficult to work with in C++?
Posted by: username_0 on 2023-11-30
C++ does not generate default copy/move constructors or assignment operators for any volatile struct or class type. That is, given these three declarations, all of the following statements are illegal:
class Foo {}; // or struct Foo {};
Foo a;
volatile Foo b;

// ALL of these are illegal:
a = b;
b = a;
Foo c = b;
Foo d = std::move(b);
volatile Foo e = b;
b = b; // a=a is useless but allowed. b=b is not permitted.

Furthermore, if you define constructors and operator=s to allow these, then all the default ones go away, and you have to explicitly default them. Defining all this is a massive amount of boilerplate even for a small PoD class:
class Bar {
  int m_a, m_b;
public:
  constexpr Bar(int a, int b) noexcept : m_a(a), m_b(b) {}
  // Everything below this line is the aforementioned boilerplate
  Bar(const Bar&) = default;
  Bar(Bar&&) = default;
  Bar& operator=(const Bar&) = default;
  Bar& operator=(Bar&&) = default;
  Bar(const volatile Bar& other) noexcept : m_a(other.m_a), m_b(other.m_b) {}
  Bar(volatile Bar&& other) noexcept : m_a(std::move(other.m_a)), m_b(std::move(other.m_b)) {}
  Bar& operator=(const volatile Bar& other) noexcept {
    m_a = other.m_a;
    m_b = other.m_b;
    return *this;
  }
  volatile Bar& operator=(const volatile Bar& other) volatile noexcept {
    m_a = other.m_a;
    m_b = other.m_b;
    return *this;
  }
  Bar& operator=(volatile Bar&& other) noexcept {
    m_a = std::move(other.m_a);
    m_b = std::move(other.m_b);
    return *this;
  }
  volatile Bar& operator=(volatile Bar&& other) volatile noexcept {
    m_a = std::move(other.m_a);
    m_b = std::move(other.m_b);
    return *this;
  }
};

Why does C++ not generate these constructors and assignment operators for volatile types? Were they just considered unnecessary, or is there some technical reason they shouldn't exist by default?
**Tags:** c++ assignment
> **Comments:**
> **username_1**: Often unnoticed: The C language guarantees that volatile objects that have been modified between setjmp() and longjmp() are not restored. Usually longjmp() will restore all registers and volatile keeps things out of registers so this will happen by coincidence, but the language actually gives a guarantee.  
> **username_2**: related cppcon talk: [Deprecating volatile - JF Bastien - CppCon 2019](https://youtu.be/KJW_DLaVXIY)  
---
## 3 answers
### Answer by username_7 on 2023-12-01. Score: 56 | Accepted answer
What volatile actually means
The meaning of volatile in C and C++ is often poorly understood. The main use case for volatile is low-level programming on embedded systems that utilize memory-mapped I/O. In these systems, portions of the address space are not mapped to ROM or RAM; instead, reads from or writes to these special addresses are arbitrarily interpreted as commands by other hardware attached to the bus. In these cases, the usual properties of memory do not apply:

When an address refers to memory, merely reading the value at that address has no side effects, so unused reads can be deleted by the compiler without changing program semantics. However, when an address refers to a memory-mapped I/O device, a “read” can actually modify the device’s state!
As a very simple hypothetical example of this, an address might be mapped to a shift register. Each time the address is read, one value might be shifted out, so repeated reads will return successive values in the shift register. “Unused” reads still advance the register, so removing them changes the program’s semantics.

Similar to the above, when an address refers to memory, reading from it multiple times with no intervening writes or synchronizations can always be assumed to return the same value. As the shift register example above illustrates, this is not necessarily true.

When an address refers to memory, writing to an address multiple times with no intervening reads or synchronizations can always be safely coalesced to a single write (of the final value). If the address refers to a memory-mapped I/O device, each store can have side effects, so this is not safe in general.

When an address refers to RAM, a write followed immediately by a read of the same memory address can always be assumed to return the written value. If the address refers to a memory-mapped I/O device, reads and writes to the same address can be interpreted in completely different ways, so there is no requirement that this be true.


In other words, reads from or writes to an address mapped to an I/O device is really more like a kind of hardware procedure call than a traditional load or store from memory. This is incompatible with the standard memory semantics of the C and C++ abstract machines, which assume that memory addresses refer to real memory and therefore behave in the usual way.
volatile is used to instruct a C or C++ compiler that a variable contains an address that is not mapped to conventional memory. This suppresses most of the compiler’s memory-related optimizations: loads and stores through volatile references are never dropped, coalesced, duplicated, or reordered. In other words, loads and stores through volatile addresses are optimized in much the same way as a call to an unknown function.
volatile is not useful for multithreading
There is a widespread (false) belief that volatile is useful in multithreaded programs to ensure mutations are consistently visible across threads, but this is not the case. volatile only requires that the compiler emit a load or store instruction for each load or store in the source program, but this is not sufficient to ensure consistent visibility across threads on hardware that uses a weak memory model. On such systems, speculative execution can reorder non-conflicting loads and stores to conventional memory, so even if volatile is used, memory barriers are still necessary to ensure consistent ordering of mutations between threads.
(In theory, a C or C++ compiler could interpret volatile differently and automatically insert memory barriers to make it useful in this way. However, this is not guaranteed by the standard for either language, and GCC and Clang do not interpret it this way. By default, MSVC does interpret it this way on x86 and x86_64, but it does not on ARM, and it inhibits useful optimizations that more targeted synchronization constructs do not, so relying on this behavior is not recommended.)
volatile being a qualifier is somewhat strange
The fact that volatile is a qualifier like const is a somewhat strange decision because it is not really a property of the variable. Instead, volatile essentially changes the meaning of the * and -> operators when used on that variable. Instead of providing the volatile keyword, C could have simply provided volatile reads and writes as separate operations. For example, instead of writing
volatile unsigned char* io_addr = 0xdeadbeef;
*io_addr = 0x01;
return *io_addr;

one could write
mio_store(0xdeadbeef, 0x01);
return mio_load(0xdeadbeef);

which would arguably make it much clearer that these are not ordinary reads and writes but more like procedure calls. Indeed, this is precisely the approach that Rust takes: it provides the std::ptr::read_volatile and std::ptr::write_volatile functions instead of providing a special qualifier.
C’s decision to treat volatile as a qualifier makes more sense when considered from a historical perspective. C’s original purpose was essentially to be a fancy portable macro assembler, and C programs were originally written by assembly programmers who expected to be able to predict the assembly that C compilers would generate. From this point of view, it makes sense to want a guarantee that loads and stores to certain variables really produce loads and stores, and this naturally led to the volatile keyword. Modern C and C++ compilers diverged from this original philosophy decades ago, so the design of volatile has unfortunately outlived its original usefulness.
volatile objects and C++ classes
All of the above describes the meaning of volatile when used on references to primitive types, like char* or int*, but C++ substantially complicates the meaning of const and volatile qualifiers, also known as cv-qualifiers. When volatile is applied to a variable of composite type like a struct or class, then references to members through that variable are considered volatile as well. Additionally, const and volatile can also be used to qualify methods, and if a method is invoked through a cv-qualified object, the method must be appropriately cv-qualified.
Given the meaning of volatile, this restriction makes sense. Keep in mind that every volatile load and store should be treated like a side-effectful procedure call, so order very much matters! This substantially complicates the meaning of copy constructors and operator=, each of which perform several sequential reads and writes to the objects’ members. Changing the order in which these assignments are performed could very well result in completely different behavior due to different ordering of effects, so it is likely better that the programmer specify that order explicitly.
All that said, by this point you should hopefully be wondering whether a volatile-qualified composite object is terribly useful in the first place. Given that volatile is generally useful with memory-mapped I/O, the addresses targeted by volatile loads and stores are generally fixed and statically-known. It makes little sense to copy an object from a volatile reference because the resource identified by a memory-mapped I/O address is a piece of actual hardware that cannot simply be duplicated. In most cases, volatile should not be used at all, and when it is, it should be used on values of primitive type, not objects.
> **Comments:**
> **username_8** (2023-12-01): "volatile is not useful for multithreading" -- Although this caveat almost certainly isn't relevant to OP's question, I do like to qualify this with "not useful at the application level". It can sometimes have a place deep in the guts of the _implementation_ of concurrency primitives. (And one can also use it correctly for sharing state with signal handlers, which are not exactly concurrency, but are not mmio either. However, now that atomics exist, one can and should use those instead.)  
> **username_7** (2023-12-01): @GlennWillen In a multithreaded context, it is truly never useful: even if atomics cannot be used (which is rare), making `volatile` useful would still require the use of memory barriers, but the ordering constraints imposed by `atomic_thread_fence` apply to non-`volatile` references (for better or for worse), so the `volatile` qualifier serves no purpose. I concede that it is theoretically useful for state used by signal handlers, though as you say, this seems more academic than practical.  
> **username_8** (2023-12-01): Hmm, I will have to weaken my claim to "I'm pretty sure once upon a time it was useful in this way in C, but perhaps they have standardized enough useful primitives to make it moot." Very good if they have. (I see that atomic_thread_fence is in C++11, and frankly I learned all the scary things I know about concurrency in C before 2011, and have mostly summarized them at this point to "don't.")  
> **username_9** (2023-12-01): "`volatile` is not useful for multithreading" – It probably doesn't help that in Java, a language with superficially C-like syntax, that is *exactly* what `volatile` is most useful for. So, when programmers come out of a "Java school" and later learn C or C++, they have this meaning engrained in their brains.  
> **username_10** (2023-12-01): Nitpick: A `volatile struct` can be useful to concretely define the layout of a MMIO region that has multiple related components to it such that you can refer to that region with a single variable or reuse the layout for multiple regions if you have multiple devices with the same layout. This can make code utilizing that MMIO region easier to read, because it groups all the bits together as one logical unit. I agree though that a `volatile class` in C++ is essentially never useful.  
> **username_0** (2023-12-01): For context, my use case is sending data between interrupt handlers and the main function. Without `volatile`, the main function won't see any changes from the interrupt handlers.  
> **username_11** (2023-12-01): @Bbrk24, sounds like you should be using multithreading synchronization constructs instead.  
> **username_12** (2023-12-01): @GlennWillen: The Linux kernel still uses `volatile` to roll its own lock-free atomics on the few compilers it supports (GCC and I think Clang which make an effort to do `volatile` accesses with a single load or store for types of register width or narrower, unlike with non-volatile types: [Which types on a 64-bit computer are naturally atomic in gnu C and gnu C++? -- meaning they have atomic reads, and atomic writes](https://stackoverflow.com/q/71866535)).  
> **username_12** (2023-12-01): @GlennWillen: Without barriers (e.g. the Linux kernel's functions that use inline asm), `volatile` is like load or store with `memory_order_relaxed`, except that only run-time reordering of volatile ops with other volatile ops is allowed, not compile-time.  Visibility between threads/cores *does* happen on all mainstream CPUs because they have coherent cache between the cores that run threads of a program or OS; see [my answer about `volatile` and the low level details](//stackoverflow.com/a/58535118). (Communication between non-cache-coherent cores like an MCU + DSP needs a manual flush.)  
> **username_12** (2023-12-01): (Often you want at least [acquire / release semantics](https://preshing.com/20120913/acquire-and-release-semantics/) for communicating between threads; that's what this answer is talking about with the phrasing "**consistent** visibility", with consistency referring to ordering of operations on different memory locations, as observed by other threads.  C++ `std::memory_order_relaxed` doesn't give you that either, and normally compiles to the same instructions as for `volatile`, except of course for atomic RMWs like `fetch_add` or `compare_exchange_weak`, which `volatile` doesn't use.)  
> **username_3** (2023-12-02): Whether it is not the best practice, I remember that sharing global variable between threads of execution was stated as a case where volatile should be used. From the C/C++ manual:

3.4.2 Use the Volatile Keyword for Necessary Memory Accesses

The compiler analyzes data flow to avoid memory accesses whenever possible. If you have code that

depends on memory accesses exactly as written in the C/C++ code, you must use the volatile keyword to

identify these accesses. The compiler does not optimize out any references to volatile variables.  
> **username_13** (2023-12-02): I think the optimizer is allowed to transform `while (atomic_bool.load());` into `if (atomic_bool.load()) while(true);`, so sometimes atomics alone may not directly do what one wants. (Not sure how to actually do that properly in C++ at the moment, I just remember that I once threw in an additional volatile to a load in llvm IR.)  
> **username_14** (2023-12-02): Thanks for this explanation, especially  about MSVC behavior on x86. I've seen the claim the volatile isn't useful for ensuring that writes are visible across threads many times but had never made the connection that this is a particular feature of the x86 (the only CPU with which I am at all familiar) rather than simply how volatile works. It may also not help that when the texts for C and C++ were first written multi-core systems really were not of concern to the typical programmer. Those texts basically said "volatile is a pessimization ensuring that all reads and writes actually occur".  
> **username_15** (2023-12-03): The benefit of `volatile` being in the type system is that the type system prevents you from accidentally attempting an ordinary access that should be a volatile access. Perhaps Rust, with its borrow checker, has some other way of preventing this; I wouldn't know.  
---
### Answer by username_6 on 2023-12-01. Score: 5
In a follow on comment, OP mentioned that his use case is on ISRs.  Because an ISR is so tightly constrained on what it can do, and the nature of interrupts guarantees their memory ordering, the following is one of the valid uses of volatile in the context of this question:
volatile sig_atomic_t isrFlag{0};

void interrupt_handler() { isrFlag = 1; }

int main() {
    for (;;) {
       if (isrFlag) {
           // Do things
       }
    }
}

There is no need for an additional memory fence here.
https://en.cppreference.com/w/c/program/sig_atomic_t
> **Comments:**
> **username_16** (2024-07-22): Note that even on a single processor system, clang and gcc would require using a memory fence or memory clobber directive to ensure that ordinary accesses which occur before a write to a `volatile sig_atomic_t` don't get reordered across it.  
---
### Answer by username_3 on 2023-12-02. Score: 1
Although this was well explained in the previous answers, I like when you can demonstrate it with a real-life example.
I admit I never saw usage of the volatile qualifier, except in the embedded software world for primitive types, i.e., pointers to memory locations.
And I don’t know what purpose it would serve in the C++ object world.
Compilers consider memory access "expensive" and try to avoid it whenever they can, by storing its content in CPU registers.
The purpose of volatile is just to tell compiler not to make any assumptions and optimizations regarding handling variables and memory access.
Basically, it must assume that the content of a memory location accessed by a pointer
can change independently of our code, at any time (asynchronously by hardware, in the ISR).
Let’s see those examples.
Here code reads some peripheral status register and checks when the content of a register changes to 0x02:
uint8_t * stat_reg = (uint8_t *) 0x08001234;
// Wait for register to become non-zero
do { ... } while (0 == *stat_reg)

The compiler doing optimization could do something like this.
Having already read the register content from the peripheral register at 0x1234,
the compiler figures there isn't any need reading from this memory location again, so the compiler places the content of memory in the CPU accumulator
and checks the accumulator instead:
mov stat_reg, #0x08001234
mov a, @stat_reg
loop:
   ...
   bz loop

Needless to say why this assembly code would never work.
So volatile is used here to tell the compiler not to make any optimizations when accessing the address the pointer points to.
uint8_t volatile * stat_reg = (uint8_t volatile *) 0x08001234

Let’s try it with a real example with the help of GDB:
#include 

int func1(volatile int* i) {
    int a = 31;

    while (*i == 0)
        a++;

    return a;
}

volatile in reg = 0;

int main() {
    return reg + func1(®);
}

If we disassemble our snippet where variable reg represents our hardware register:
   0x000055555555512d <+4>:     mov    (%rdi),%eax
   0x000055555555512f <+6>:     test   %eax,%eax
   0x0000555555555131 <+8>:     jne    0x555555555142 
   0x0000555555555133 <+10>:    mov    $0x1f,%eax
   0x0000555555555138 <+15>:    add    $0x1,%eax
=> 0x000055555555513b <+18>:    mov    (%rdi),%edx
   0x000055555555513d <+20>:    test   %edx,%edx
   0x000055555555513f <+22>:    je     0x555555555138 

We see that in each iteration value of volatile register, (which address is in $rdi) will be loaded from memory, mov (%rdi),%edx, and tested, test %edx,%edx.
Now if we recompile it without the volatile qualifier, the compiler assumes that the value of reg never changes, and there isn't any need for reading from memory in each iteration, so the while loop condition is always true.
We end with an infinite loop in optimized code:
   0x000055555555512d <+4>:     cmpl   $0x0,(%rdi)
   0x0000555555555130 <+7>:     jne    0x555555555134 
=> 0x0000555555555132 <+9>:     jmp    0x555555555132 
   0x0000555555555134 <+11>:    mov    $0x1f,%eax
> **Comments:**
> **username_0** (2023-12-02): I mentioned this in the comments on another answer, but I’m using the volatile objects to communicate between interrupt handlers and the main function. The interrupt handlers check certain sensors and then write the sensor data to a global variable, and unless that variable is `volatile` the main function will never see the sensor data.  
> **username_4** (2023-12-02): Welcome. I am confused as to why you posted a screenshot of the code as opposed to the text of the code. Besides that, this is a great first answer.  
> **username_3** (2023-12-02): Sorry, is there some other reason against posting screenshot, or just because you cannot copy example?  
> **username_0** (2023-12-02): @DrazenGrasovec Posting a screenshot requires more data for others to load and isn't as easy to read for visually impaired users.  
> **username_5** (2023-12-02): Accessibility is the main reason not to post text/code as images, as Bbrk mentioned. As well as the fact that some people can't access Imgur/have images disabled.  
> **username_3** (2023-12-03): OK. I admit didn't considered that. I will keep to  that rule in future  
> **username_6** (2023-12-04): @Bbrk24 I think there's a lot here that's superfluous to your actual question.  My answer is essentially the *only* legal thing an ISR can do in C.  `volatile' here tells the compiler that it must actually read the variable (all reads and writes are treated as sequenced side-effects), and can't re-order it at compile time in regards to other sequenced side-effects. (Conforming programs before C++11 were single-threaded).

Since C++11 and a C++ thread model, strictly conforming programs can use std::atomic and std::atomic_signal_fence as well, which is appropriate for multithreaded use.  
---
