Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>No. However, most of the time they will be.</p> <p>While it's helpful to think of <code>const</code> as "thread-safe" and <code>mutable</code> as "(already) thread-safe", <code>const</code> is still fundamentally tied to the notion of promising "I won't change this value". It always will be.</p> <p>I have a long-ish train of thought so bear with me.</p> <p>In my own programming, I put <code>const</code> everywhere. If I have a value, it's a bad thing to change it unless I say I want to. If you try to purposefully modify a const-object, you get a compile-time error (easy to fix and no shippable result!). If you accidentally modify a non-const object, you get a runtime programming error, a bug in a compiled application, and a headache. So it's better to err on the former side and keep things <code>const</code>.</p> <p>For example: </p> <pre><code>bool is_even(const unsigned x) { return (x % 2) == 0; } bool is_prime(const unsigned x) { return /* left as an exercise for the reader */; } template &lt;typename Iterator&gt; void print_special_numbers(const Iterator first, const Iterator last) { for (auto iter = first; iter != last; ++iter) { const auto&amp; x = *iter; const bool isEven = is_even(x); const bool isPrime = is_prime(x); if (isEven &amp;&amp; isPrime) std::cout &lt;&lt; "Special number! " &lt;&lt; x &lt;&lt; std::endl; } } </code></pre> <p>Why are the parameter types for <code>is_even</code> and <code>is_prime</code> marked <code>const</code>? Because from an implementation point of view, changing the number I'm testing would be an error! Why <code>const auto&amp; x</code>? Because I don't intend on changing that value, and I want the compiler to yell at me if I do. Same with <code>isEven</code> and <code>isPrime</code>: the result of this test should not change, so enforce it.</p> <p>Of course <code>const</code> member functions are merely a way to give <code>this</code> a type of the form <code>const T*</code>. It says "it would be an error in implementation if I were to change some of my members". </p> <p><code>mutable</code> says "except me". This is where the "old" notion of "logically const" comes from. Consider the common use-case he gave: a mutex member. You <em>need</em> to lock this mutex to ensure your program is correct, so you need to modify it. You don't want the function to be non-const, though, because it would be an error to modify any other member. So you make it <code>const</code> and mark the mutex as <code>mutable</code>.</p> <p><em>None of this has to do with thread-safety.</em></p> <p>I think it's one step too far to say the new definitions replace the old ideas given above; they merely compliment it from another view, that of thread-safety. </p> <p>Now the point of view Herb gives that if you have <code>const</code> functions, they need to be thread-safe to be safely usable by the standard library. As a corollary of this, the only members you should really mark as <code>mutable</code> are those that are already thread-safe, because they are modifiable from a <code>const</code> function:</p> <pre><code>struct foo { void act() const { mNotThreadSafe = "oh crap! const meant I would be thread-safe!"; } mutable std::string mNotThreadSafe; }; </code></pre> <p>Okay, so we know that thread-safe things <em>can</em> be marked as <code>mutable</code>, you ask: should they be?</p> <p>I think we have to consider both view simultaneously. From Herb's new point of view, yes. They are thread safe so do not need to be bound by the const-ness of the function. But just because they <em>can</em> safely be excused from the constraints of <code>const</code> doesn't mean they have to be. I still need to consider: would it be an error in implementation if I did modify that member? If so, it needs to not be <code>mutable</code>!</p> <p>There's a granularity issue here: some functions may need to modify the would-be <code>mutable</code> member while others don't. This is like wanting only some functions to have friend-like access, but we can only friend the entire class. (It's a language design issue.)</p> <p>In this case, you should err on the side of <code>mutable</code>.</p> <p>Herb spoke just slightly too loosely when he gave a <code>const_cast</code> example an declared it safe. Consider:</p> <pre><code>struct foo { void act() const { const_cast&lt;unsigned&amp;&gt;(counter)++; } unsigned counter; }; </code></pre> <p>This is safe under most circumstances, except when the <code>foo</code> object itself is <code>const</code>:</p> <pre><code>foo x; x.act(); // okay const foo y; y.act(); // UB! </code></pre> <p>This is covered elsewhere on SO, but <code>const foo</code>, implies the <code>counter</code> member is also <code>const</code>, and modifying a <code>const</code> object is undefined behavior.</p> <p>This is why you should err on the side of <code>mutable</code>: <code>const_cast</code> does not quite give you the same guarantees. Had <code>counter</code> been marked <code>mutable</code>, it wouldn't have been a <code>const</code> object.</p> <p>Okay, so if we need it <code>mutable</code> in one spot we need it everywhere, and we just need to be careful in the cases where we don't. Surely this means all thread-safe members should be marked <code>mutable</code> then?</p> <p>Well no, because not all thread-safe members are there for internal synchronization. The most trivial example is some sort of wrapper class (not always best practice but they exist):</p> <pre><code>struct threadsafe_container_wrapper { void missing_function_I_really_want() { container.do_this(); container.do_that(); } const_container_view other_missing_function_I_really_want() const { return container.const_view(); } threadsafe_container container; }; </code></pre> <p>Here we are wrapping <code>threadsafe_container</code> and providing another member function we want (would be better as a free function in practice). No need for <code>mutable</code> here, the correctness from the old point of view utterly trumps: in one function I'm modifying the container <em>and that's okay because I didn't say I wouldn't</em> (omitting <code>const</code>), and in the other I'm not modifying the container <em>and ensure I'm keeping that promise</em> (omitting <code>mutable</code>).</p> <p>I think Herb is arguing the most cases where we'd use <code>mutable</code> we're also using some sort of internal (thread-safe) synchronization object, and I agree. Ergo his point of view works most of the time. But there exist cases where I simply <em>happen</em> to have a thread-safe object and merely treat it as yet another member; in this case we fall back on the old and fundamental use of <code>const</code>.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload