<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://quuxplusone.github.io/blog/feed.xml" rel="self" type="application/atom+xml" /><link href="https://quuxplusone.github.io/blog/" rel="alternate" type="text/html" /><updated>2026-04-14T02:15:32+00:00</updated><id>https://quuxplusone.github.io/blog/feed.xml</id><title type="html">Arthur O’Dwyer</title><subtitle>Stuff mostly about C++</subtitle><entry><title type="html">`auto{x} != auto(x)`</title><link href="https://quuxplusone.github.io/blog/2026/04/11/auto-x-with-curly-braces/" rel="alternate" type="text/html" title="`auto{x} != auto(x)`" /><published>2026-04-11T00:01:00+00:00</published><updated>2026-04-11T00:01:00+00:00</updated><id>https://quuxplusone.github.io/blog/2026/04/11/auto-x-with-curly-braces</id><content type="html" xml:base="https://quuxplusone.github.io/blog/2026/04/11/auto-x-with-curly-braces/"><![CDATA[<p>Recently it was asked: What’s the difference between the expressions <code class="language-plaintext highlighter-rouge">auto(x)</code>
and <code class="language-plaintext highlighter-rouge">auto{x}</code> in C++23?</p>

<p>The construct <code class="language-plaintext highlighter-rouge">auto(x)</code> arrived via
<a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0849r8.html">P0849 “<em>decay-copy</em> in the language”</a>.
We could already write direct-initialization to a <em>named</em> type as either a declaration
or a cast-expression:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>T y(x); // declaration
return T(x); // cast-expression
</code></pre></div></div>

<p>P0849 just extended this syntax to work for a <em>placeholder</em> type as well:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>auto y(x); // declaration
return auto(x); // cast-expression
</code></pre></div></div>

<p>Both of the latter lines mean “Deduce the type of <code class="language-plaintext highlighter-rouge">auto</code> from the type of <code class="language-plaintext highlighter-rouge">x</code> (decaying to
a non-array object type if necessary) — let’s call that type <code class="language-plaintext highlighter-rouge">T</code> — and then explicitly cast <code class="language-plaintext highlighter-rouge">x</code> to
that type exactly as if the user had written <code class="language-plaintext highlighter-rouge">T</code> in place of <code class="language-plaintext highlighter-rouge">auto</code>.”</p>

<p>This <em>usually</em> means we’re just making a copy of <code class="language-plaintext highlighter-rouge">x</code> using its copy constructor.
If <code class="language-plaintext highlighter-rouge">x</code> stands for an xvalue expression, we’re calling the move constructor, and if
<code class="language-plaintext highlighter-rouge">x</code> is a prvalue, we’re probably not doing anything at all. <code class="language-plaintext highlighter-rouge">auto(x)</code> is simple.</p>

<p>But <code class="language-plaintext highlighter-rouge">auto{x}</code> is more complicated, because curly braces produce an initializer list.
This means the same thing as <code class="language-plaintext highlighter-rouge">T{x}</code>: “given the list of elements <code class="language-plaintext highlighter-rouge">{x}</code>, make me a <code class="language-plaintext highlighter-rouge">T</code>
with those elements.” As <a href="https://eel.is/c++draft/dcl.init.list#3.7">[dcl.init.list]/3.7</a> shows,
that’s not always the same thing as “make me a copy of <code class="language-plaintext highlighter-rouge">x</code>.” <a href="https://godbolt.org/z/75MEveMPW">Godbolt</a>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>auto paren() {
    std::vector&lt;std::any&gt; v;
    return auto(v);
}

auto curly() {
    std::vector&lt;std::any&gt; v;
    return auto{v};
}
</code></pre></div></div>

<p>The former means “make an empty vector, then return a copy of that vector (with no elements).”
The latter means “make an empty vector, then return a vector <em>containing</em> that vector (with one element).”</p>

<ul>
  <li>
    <p>As of this writing, MSVC gets this wrong: it calls the copy constructor in both cases.
  But <a href="https://eel.is/c++draft/dcl.init.list#3.7">[dcl.init.list]/3.7</a> (last improved by
  <a href="https://cplusplus.github.io/CWG/issues/2638.html">CWG2638</a>) makes it very clear that
  MSVC is in the wrong.</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">return (x);</code> implicitly moves from <code class="language-plaintext highlighter-rouge">x</code> (see <a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2266r3.html">P2266 “Simpler implicit move”</a>),
  but <code class="language-plaintext highlighter-rouge">return auto(x);</code> does not. This makes sense, because <code class="language-plaintext highlighter-rouge">return T(x);</code> doesn’t move-from <code class="language-plaintext highlighter-rouge">x</code> either.
  Remember, all <code class="language-plaintext highlighter-rouge">auto</code> does here is hold the place of an explicitly specified <code class="language-plaintext highlighter-rouge">T</code>.</p>
  </li>
</ul>

<p>Consider also these variations:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>auto p = (v); // copy
auto c = {v}; // initialize a new vector of 1 element

auto p(v); // copy
auto c{v}; // initialize a new vector of 1 element (MSVC gets this wrong)
</code></pre></div></div>

<p>Of course <code class="language-plaintext highlighter-rouge">vector&lt;any&gt;</code> is a pathological case. Its benefit is that it’s also a <em>simple</em> case
using only STL types, in case you ever need to demonstrate the difference between <code class="language-plaintext highlighter-rouge">(x)</code> and <code class="language-plaintext highlighter-rouge">{x}</code>
to anyone else.</p>

<p>The takeaway: As always, you should use curly braces when you have a sequence of elements
(such as when initializing an aggregate or a container); if you aren’t in that situation (such as when
you’re writing generic code) you should use ordinary parentheses.
See <a href="/blog/2019/02/18/knightmare-of-initialization/">“The Knightmare of Initialization in C++”</a> (2019-02-18).</p>

<hr />

<p>I suspect there is no situation where it ever makes sense to use <code class="language-plaintext highlighter-rouge">auto{x}</code> in real code.
But I’m glad it exists in the language, for symmetry and consistency with <code class="language-plaintext highlighter-rouge">T{x}</code>.</p>

<p>Note that all of these lines—</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>auto a(1,2,3);
auto a = auto(1,2,3);
auto a{1,2,3};
auto a = auto{1,2,3};
</code></pre></div></div>

<p>—are invalid C++. You can never direct-initialize <code class="language-plaintext highlighter-rouge">auto</code> with multiple arguments.
However, both of the following copy-initializations are legal and silly:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>auto i = (1,2,3); // comma operator; i is int
auto i = {1,2,3}; // i is initializer_list&lt;int&gt;
</code></pre></div></div>

<p>The latter is a historical accident which is supported these days, as far as I know, <em>only</em> so that we can specify the behavior
of <code class="language-plaintext highlighter-rouge">for (int i : {1,2,3})</code> without having to write a special case into <a href="https://eel.is/c++draft/stmt.ranged">[stmt.ranged]</a>.
(<a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3922.html">N3922 “New rules for auto deduction from braced-init-list”</a>
is the paper that removed <code class="language-plaintext highlighter-rouge">auto a{1,2,3}</code> from the language. N3922 came in 2014, at the height of the “Almost Always Auto” and “Uniform Initialization”
fads; it was widely assumed that newbies would write <code class="language-plaintext highlighter-rouge">auto a{1,2}</code> and shoot themselves in the foot, but writing <code class="language-plaintext highlighter-rouge">auto a = {1,2}</code>
wasn’t so attractive to newbies and thus wasn’t treated so urgently as a footgun. At the same time, N3922 changed both
<code class="language-plaintext highlighter-rouge">auto a{1}</code> and <code class="language-plaintext highlighter-rouge">auto a = {1}</code> to deduce <code class="language-plaintext highlighter-rouge">int</code> rather than <code class="language-plaintext highlighter-rouge">initializer_list&lt;int&gt;</code>. Only <code class="language-plaintext highlighter-rouge">auto a = {1,2}</code> remains as a special case
inconsistent with the rest of the language. <a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3912.html">N3912 §1</a> says
this special case will be useful to “advanced users,” which I think in hindsight was a bad justification for keeping it.)</p>]]></content><author><name></name></author><category term="c++-learner-track" /><category term="c++-style" /><category term="initialization" /><category term="initializer-list" /><summary type="html"><![CDATA[Recently it was asked: What’s the difference between the expressions auto(x) and auto{x} in C++23?]]></summary></entry><entry><title type="html">The “macro overloading” idiom</title><link href="https://quuxplusone.github.io/blog/2026/04/02/macro-overloading/" rel="alternate" type="text/html" title="The “macro overloading” idiom" /><published>2026-04-02T00:01:00+00:00</published><updated>2026-04-02T00:01:00+00:00</updated><id>https://quuxplusone.github.io/blog/2026/04/02/macro-overloading</id><content type="html" xml:base="https://quuxplusone.github.io/blog/2026/04/02/macro-overloading/"><![CDATA[<p>Here’s a neat trick to create an “overloaded macro” in C, such that
<code class="language-plaintext highlighter-rouge">M(x)</code> does one thing and <code class="language-plaintext highlighter-rouge">M(x, y)</code> does something else. For example,
we could make a macro <code class="language-plaintext highlighter-rouge">ARCTAN</code> such that <code class="language-plaintext highlighter-rouge">ARCTAN(v)</code> calls <code class="language-plaintext highlighter-rouge">atan(v)</code>
and <code class="language-plaintext highlighter-rouge">ARCTAN(y,x)</code> calls <code class="language-plaintext highlighter-rouge">atan2(y,x)</code>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#define GET_ARCTAN_MACRO(_1, _2, x, ...) x
#define ARCTAN(...) GET_ARCTAN_MACRO(__VA_ARGS__, atan2, atan)(__VA_ARGS__)
</code></pre></div></div>

<p>So <code class="language-plaintext highlighter-rouge">ARCTAN(1)</code> expands to <code class="language-plaintext highlighter-rouge">GET_ARCTAN_MACRO(1, atan2, atan)(1)</code> expands to <code class="language-plaintext highlighter-rouge">atan(1)</code>,
while <code class="language-plaintext highlighter-rouge">ARCTAN(2,3)</code> expands to <code class="language-plaintext highlighter-rouge">GET_ARCTAN_MACRO(2,3, atan2, atan)(2,3)</code> expands to <code class="language-plaintext highlighter-rouge">atan2(2,3)</code>.</p>

<p>Or again, to make an “overloaded” <code class="language-plaintext highlighter-rouge">HYPOT</code> macro:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#define GET_HYPOT_MACRO(_1, _2, _3, x, ...) x
#define HYPOT(...) GET_HYPOT_MACRO(__VA_ARGS__, hypot3, hypot, )(__VA_ARGS__)
</code></pre></div></div>

<p>So <code class="language-plaintext highlighter-rouge">HYPOT(x)</code> expands to <code class="language-plaintext highlighter-rouge">(x)</code>, <code class="language-plaintext highlighter-rouge">HYPOT(x,y)</code> expands to <code class="language-plaintext highlighter-rouge">hypot(x,y)</code>, and <code class="language-plaintext highlighter-rouge">HYPOT(x,y,z)</code>
expands to <code class="language-plaintext highlighter-rouge">hypot3(x,y,z)</code>.</p>

<ul>
  <li>
    <p><code class="language-plaintext highlighter-rouge">HYPOT(1,2,3,4)</code> expands to <code class="language-plaintext highlighter-rouge">GET_HYPOT_MACRO(1,2,3,4, hypot3, hypot,)(1,2,3,4)</code>
  expands to <code class="language-plaintext highlighter-rouge">4(1,2,3,4)</code>, which is garbage. It’s likely to be ill-formed garbage, though,
  so that’s not too user-unfriendly.</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">HYPOT()</code> expands to <code class="language-plaintext highlighter-rouge">GET_HYPOT_MACRO(,hypot3,hypot,)()</code> expands to <code class="language-plaintext highlighter-rouge">()</code>. <code class="language-plaintext highlighter-rouge">ARCTAN()</code>
  expands to <code class="language-plaintext highlighter-rouge">GET_ARCTAN_MACRO(, atan2, atan)()</code> expands to <code class="language-plaintext highlighter-rouge">atan()</code>. These are less
  user-friendly.</p>
  </li>
</ul>

<p>If you don’t mind relying on a C23/C++20 preprocessor feature, you can improve the latter
experience:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#define GET_ARCTAN_MACRO(_1, _2, x, ...) x
#define ARCTAN(...) GET_ARCTAN_MACRO(__VA_ARGS__ __VA_OPT__(,) atan2, atan)(__VA_ARGS__)
</code></pre></div></div>

<p>Now <code class="language-plaintext highlighter-rouge">ARCTAN()</code> expands to <code class="language-plaintext highlighter-rouge">GET_ARCTAN_MACRO(atan2, atan)()</code> which is more cleanly ill-formed.
(It has too few macro arguments.)</p>

<blockquote>
  <p>You might think you could use <a href="https://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html#:~:text=has%20a%20special%20meaning">a well-known GCC extension</a>
to write <code class="language-plaintext highlighter-rouge">__VA_ARGS__##,</code> — but no, the token-paste operator <code class="language-plaintext highlighter-rouge">##</code> has its special meaning
only within <code class="language-plaintext highlighter-rouge">,##__VA_ARGS__</code>, not within <code class="language-plaintext highlighter-rouge">__VA_ARGS__##,</code>.</p>
</blockquote>

<p>Boost.Preprocessor implements <a href="https://www.boost.org/doc/libs/latest/libs/preprocessor/doc/ref/variadic_size.html"><code class="language-plaintext highlighter-rouge">BOOST_PP_VARIADIC_SIZE</code></a>
via a minor variation on this idiom:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#define GET_SIZE_MACRO(_1, _2, _3, _4, _5, x, ...) x
#define SIZE(...) GET_SIZE_MACRO(__VA_ARGS__ __VA_OPT__(,) 5,4,3,2,1,0)
</code></pre></div></div>

<p>Hat tip to <a href="https://www.quarterstar.tech/2026/03/30/rust-to-cpp-implementing-the-question-mark-operator#the-implementation">this blog post by “Quarterstar”</a>
(March 2026); the technique is also shown by <a href="https://codelucky.com/c-macros/#6_Macro_Overloading">CodeLucky</a> (September 2024),
on <a href="https://stackoverflow.com/questions/11761703/overloading-macro-on-number-of-arguments">StackOverflow</a> (2012),
and presumably much older places.
<a href="https://github.com/search?q=%2FGET_.*_MACRO%5B%28%5D__VA_ARGS__%2F&amp;type=code">GitHub search</a> turns up many cases of the pattern,
even without considering variations in the <code class="language-plaintext highlighter-rouge">GET_*_MACRO</code> naming convention.</p>

<p>Caveat: As of 2026, MSVC’s preprocessor can’t handle this trick by default.
You have to tell it to behave conformingly, by adding <a href="https://learn.microsoft.com/en-us/cpp/build/reference/zc-preprocessor"><code class="language-plaintext highlighter-rouge">-Zc:preprocessor</code></a>
to your command line. (This is also how you get it to recognize <code class="language-plaintext highlighter-rouge">__VA_OPT__</code>!)
Alternatively, MSVC’s old non-conforming preprocessor will accept the code as long as it’s wrapped in
an additional layer of indirection. See
<a href="/blog/2018/06/18/fundamental-theorem-of-software-engineering/">“The Fundamental Theorem of Software Engineering”</a> (2018-06-18).</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// Work around MSVC's non-conforming preprocessor
#define EXPAND(x) x
#define GET_ARCTAN_MACRO(_1, _2, x, ...) x
#define ARCTAN(...) EXPAND(GET_ARCTAN_MACRO(__VA_ARGS__, atan2, atan)(__VA_ARGS__))
</code></pre></div></div>]]></content><author><name></name></author><category term="msvc" /><category term="preprocessor" /><summary type="html"><![CDATA[Here's a neat trick to create an "overloaded macro" in C, such that `M(x)` does one thing and `M(x, y)` does something else. For example, we could make a macro `ARCTAN` such that `ARCTAN(v)` calls `atan(v)` and `ARCTAN(y,x)` calls `atan2(y,x)`. #define GET_ARCTAN_MACRO(_1, _2, x, ...) x #define ARCTAN(...) GET_ARCTAN_MACRO(__VA_ARGS__, atan2, atan)(__VA_ARGS__)]]></summary></entry><entry><title type="html">Chromium’s `span`-over-initializer-list success story</title><link href="https://quuxplusone.github.io/blog/2026/03/19/p2447-success-story/" rel="alternate" type="text/html" title="Chromium’s `span`-over-initializer-list success story" /><published>2026-03-19T00:02:00+00:00</published><updated>2026-03-19T00:02:00+00:00</updated><id>https://quuxplusone.github.io/blog/2026/03/19/p2447-success-story</id><content type="html" xml:base="https://quuxplusone.github.io/blog/2026/03/19/p2447-success-story/"><![CDATA[<p>Previously: <a href="/blog/2021/10/03/p2447-span-from-initializer-list/">“<code class="language-plaintext highlighter-rouge">span</code> should have a converting constructor from <code class="language-plaintext highlighter-rouge">initializer_list</code>”</a>
(2021-10-03). This converting constructor was added by <a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2447r6.html">P2447</a>
for C++26. Way back in 2024, Peter Kasting added the same constructor to Chromium’s
<a href="https://github.com/chromium/chromium/blob/4aa6967/base/containers/span.h#L1166-L1171"><code class="language-plaintext highlighter-rouge">base::span</code></a> —
he emailed me about it at the time — but I was only recently reminded that in
<a href="https://old.reddit.com/r/cpp/comments/1n8fbg8/showcasing_underappreciated_proposals/">the /r/cpp thread</a>
about the feature he’d written:</p>

<blockquote>
  <p>Yup, this change was so useful it led to me doing a ton of reworking of Chromium’s
<code class="language-plaintext highlighter-rouge">base::span</code> just so I could implement it there.</p>
</blockquote>

<p>Speaking of <a href="/blog/2026/03/19/seven-types-of-ambiguity/">ambiguity</a>: out of context that comment <em>could</em> be taken
as sarcasm. What programmer enjoys “doing a ton of reworking just” to implement a single new constructor?
Did he mean the change was so <em>useful</em>, or, like, “<em>so</em> useful”? :) So it’s worthwhile to track down
<a href="https://github.com/chromium/chromium/commit/7a129f92f54dafe6c3ef98030ebbdbc2704d3411">pkasting’s actual commit</a>
from November 2024 and see all the places he sincerely did clean up as a result.</p>

<p>What follows is a “close reading” of all the client call sites changed in Chromium commit
<a href="https://github.com/chromium/chromium/commit/7a129f92f54dafe6c3ef98030ebbdbc2704d3411">7a129f92f5</a>.</p>

<hr />

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>std::vector&lt;scoped_refptr&lt;const Cert&gt;&gt; certs(
    {kcer_cert_0, kcer_cert_1, kcer_cert_2, kcer_cert_3, kcer_cert_3,
     kcer_cert_2, kcer_cert_1, kcer_cert_0, kcer_cert_0, kcer_cert_2,
     kcer_cert_3, kcer_cert_1});
CertCache cache(certs);
</code></pre></div></div>

<p>becomes simply</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>CertCache cache({kcer_cert_0, kcer_cert_1, kcer_cert_2, kcer_cert_3,
                 kcer_cert_3, kcer_cert_2, kcer_cert_1, kcer_cert_0,
                 kcer_cert_0, kcer_cert_2, kcer_cert_3, kcer_cert_1});
</code></pre></div></div>

<p>This is the poster-child use-case: the new code directly views a stack-allocated
<code class="language-plaintext highlighter-rouge">initializer_list</code>, where the old code had wasted time and memory copying the contents
of that <code class="language-plaintext highlighter-rouge">initializer_list</code> into a heap-allocated <code class="language-plaintext highlighter-rouge">vector</code>.
This being test code, we don’t really care about the new code’s improved efficiency,
but we do care about its improved readability and convenience.</p>

<hr />

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ASSERT_TRUE(ConfigureAppContainerSandbox(
    std::array&lt;const base::FilePath*, 2&gt;{&amp;pathA, &amp;pathB}));
</code></pre></div></div>

<p>becomes simply</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ASSERT_TRUE(ConfigureAppContainerSandbox({&amp;pathA, &amp;pathB}));
</code></pre></div></div>

<hr />

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>EXPECT_THAT(MapThenFilterStrings(
                {{"en", "de"}},
                base::BindRepeating(~~~~)),
            IsEmpty());
</code></pre></div></div>

<p>replaces its double-braces with single-braces.</p>

<hr />

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>FetchImagesForURLs(base::span_from_ref(card_art_url),
                   base::span({AutofillImageFetcherBase::ImageSize::kSmall,
                               AutofillImageFetcherBase::ImageSize::kLarge}));
</code></pre></div></div>

<p>becomes simply</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>FetchImagesForURLs(base::span_from_ref(card_art_url),
                   {AutofillImageFetcherBase::ImageSize::kSmall,
                    AutofillImageFetcherBase::ImageSize::kLarge});
</code></pre></div></div>

<p>Notice that these preceding three examples all had the same <em>intent</em> — to view a
fixed list of two items — but in the absence of natural syntax they invented three
different workarounds to imperfectly express their intent. (Temporary <code class="language-plaintext highlighter-rouge">std::array</code>;
doubled curly braces; explicit cast to <code class="language-plaintext highlighter-rouge">base::span</code>.) All three converged on the natural
syntax as soon as it became available.
Two of them benefit from <a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2752r3.html">P2752</a>, too.</p>

<hr />

<p>There were two “failure stories” in Peter’s commit, both due to the
new constructor’s lack of CTAD. (I still don’t think anyone should
ever use CTAD, and LEWG was a little scared of adding it here anyway.)
For example Peter rewrote</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>if (base::span(box.type) == base::span({'f', 't', 'y', 'p'}))
</code></pre></div></div>

<p>into</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>if (base::span(box.type) == base::span&lt;const char&gt;({'f', 't', 'y', 'p'}))
</code></pre></div></div>

<p>Now, you might think after P2447 this could have become simply</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>if (base::span(box.type) == {'f', 't', 'y', 'p'})
</code></pre></div></div>

<p>but sadly no, for <a href="https://stackoverflow.com/questions/11420448/initializer-lists-and-rhs-of-operators">historical reasons</a>
a braced initializer list is grammatically disallowed
after most C++ operators (the exceptions being <a href="https://eel.is/c++draft/expr.yield#nt:yield-expression"><code class="language-plaintext highlighter-rouge">co_yield</code></a>
and <a href="https://eel.is/c++draft/expr.assign#nt:assignment-expression">the assignment operators</a>).
I myself would probably have written one of</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>if (std::string_view(box.type, 4) == "ftyp")

if (memcmp(box.type, "ftyp", 4) == 0)
</code></pre></div></div>

<p>In the other “failure case,” Peter rewrote</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>hosts[DnsHostsKey("localhost", ADDRESS_FAMILY_IPV4)] =
    IPAddress({192, 168, 1, 1});
</code></pre></div></div>

<p>into</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>hosts[DnsHostsKey("localhost", ADDRESS_FAMILY_IPV4)] =
    IPAddress(base::span&lt;const uint8_t&gt;({192, 168, 1, 1}));
</code></pre></div></div>

<p>The trick here is that the old code (<a href="https://godbolt.org/z/hfYTqh3GW">Godbolt</a>)
wasn’t actually constructing a <code class="language-plaintext highlighter-rouge">span</code> at all; it was calling
<a href="https://github.com/chromium/chromium/blob/3afe88cc17a748340a53c3eea07fb706a5054af7/net/base/ip_address.h#L135-L137"><code class="language-plaintext highlighter-rouge">IPAddress</code>’s four-argument converting constructor</a>
followed by a redundant explicit cast to <code class="language-plaintext highlighter-rouge">IPAddress</code>.
Personally I would have preserved the old behavior and improved readability
at the same time by simply removing the curly braces:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>hosts[DnsHostsKey("localhost", ADDRESS_FAMILY_IPV4)] =
    IPAddress(192, 168, 1, 1);
</code></pre></div></div>

<p>UPDATE, 2026-03-27: After reading this blog post, Peter improved both of these “failure cases”
in the suggested ways; see <a href="https://github.com/chromium/chromium/commit/7107d5e85797db9ddc5382b90c6d0d3f0dde5509">commit 7107d5e857</a>!</p>]]></content><author><name></name></author><category term="c++-style" /><category term="initializer-list" /><category term="parameter-only-types" /><category term="proposal" /><summary type="html"><![CDATA[Previously: ["`span` should have a converting constructor from `initializer_list`"](/blog/2021/10/03/p2447-span-from-initializer-list/) (2021-10-03). This converting constructor was added by [P2447](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2447r6.html) for C++26. Way back in 2024, Peter Kasting added the same constructor to Chromium's [`base::span`](https://github.com/chromium/chromium/blob/4aa6967/base/containers/span.h#L1166-L1171) — he emailed me about it at the time — but I was only recently reminded that in [the /r/cpp thread](https://old.reddit.com/r/cpp/comments/1n8fbg8/showcasing_underappreciated_proposals/) about the feature he'd written: > Yup, this change was so useful it led to me doing a ton of reworking of Chromium's > `base::span` just so I could implement it there. Speaking of [ambiguity](/blog/2026/03/19/seven-types-of-ambiguity/): out of context that comment _could_ be taken as sarcasm. What programmer enjoys "doing a ton of reworking just" to implement a single new constructor? Did he mean the change was so _useful_, or, like, "_so_ useful"? :) So it's worthwhile to track down [pkasting's actual commit](https://github.com/chromium/chromium/commit/7a129f92f54dafe6c3ef98030ebbdbc2704d3411) from November 2024 and see all the places he sincerely did clean up as a result.]]></summary></entry><entry><title type="html">_Seven Types of Ambiguity_ (1930)</title><link href="https://quuxplusone.github.io/blog/2026/03/19/seven-types-of-ambiguity/" rel="alternate" type="text/html" title="_Seven Types of Ambiguity_ (1930)" /><published>2026-03-19T00:01:00+00:00</published><updated>2026-03-19T00:01:00+00:00</updated><id>https://quuxplusone.github.io/blog/2026/03/19/seven-types-of-ambiguity</id><content type="html" xml:base="https://quuxplusone.github.io/blog/2026/03/19/seven-types-of-ambiguity/"><![CDATA[<p>The other week I read <a href="https://en.wikipedia.org/wiki/William_Empson">William Empson’s</a>
<a href="https://en.wikipedia.org/wiki/Seven_Types_of_Ambiguity"><em>Seven Types of Ambiguity</em></a>
(first published 1930, third edition 1953 with new footnotes by the author).
Empson purports to taxonomize all sorts of “ambiguity,” defined as
any technique or nuance “which gives room for alternative reactions
to the same piece of language.” Really I’m not sure I fully grasped his
taxonomy. (One might call it a taxonomy of ambiguity in more ways than one!)
But it’s something like this:</p>

<ol>
  <li>
    <p>“A detail is effective in several ways at once.”
For example, Shakespeare’s “bare ruined choirs” is effective not just because
choirs are places in which to sing but because they involve sitting in rows,
because they are made of wood, “because the cold and Narcissistic charm of
choir-boys suits well Shakespeare’s feeling for the object of the Sonnets,”
and so on.</p>
  </li>
  <li>
    <p>“Two or more meanings are fully resolved into one.”
Shakespeare’s <a href="https://en.wikipedia.org/wiki/Sonnet_81">81st sonnet</a> is like
one of those poems that says something different when you read <a href="https://en.wikipedia.org/wiki/Reversible_poem">from bottom up</a>,
or read only <a href="https://archive.org/details/odditiescuriosit0000char/page/65">every other line</a>:
in Sonnet 81 each line could pertain to the one
after or the one before. Is it “Tongues to be your being shall rehearse /
When all the breathers of this world are dead,” or “When all the breathers
of this world are dead / You still shall live”?</p>
  </li>
  <li>
    <p>“Two apparently unconnected meanings are given simultaneously.”
For example, Dryden often “seems to claim only to be saying one thing,
even when one does not know which of two things he is saying.”
“The welkin pitched with sullen clouds around” — are the clouds
<em>pitched</em> in a circle around the edges of the sky, as the tents of the enemy?
or do the clouds <em>blacken</em> the sky?
“The praisèd peacock is not half so proud” — is this “the peacock, which
is commonly praised,” or “the peacock when it has just been praised”?
Thomas Hood’s <a href="https://archive.org/details/comicpoems00hoodgoog/page/n328">“Death in the Kitchen”</a>
is quoted for its puns and not-quite-puns.</p>
  </li>
  <li>
    <p>“Alternative meanings combine to make clear a complicated state of mind in the author.”
For example, Shakespeare’s <a href="https://en.wikipedia.org/wiki/Sonnet_83">83rd sonnet</a>:
“One must pause before shadowing with irony this noble compound of eulogy and apology.
But…” Empson does.</p>
  </li>
  <li>
    <p>“Fortunate confusion”: The author discovers his idea as he goes.
For example, Shelley’s “Ode to a Skylark”: “Like a star of Heaven
in the broad daylight thou art unseen, but yet I hear thy shrill delight /
keen as are the arrows of that silver sphere whose intense lamp narrows
in the white dawn clear …” What is “that silver sphere”? Surely the unseen star.
But in the next stanza, by association, the poet has decided the bird
is like the <em>moon</em>, which “rains out her beams” from behind a cloud.
Empson: “Mr. Eliot complained [in
<a href="https://archive.org/details/sim_dial_1928-03_84/page/249"><em>The Dial</em> of March 1928</a>]
that Shelley had mixed up two of these periods [even, broad daylight, dawn, and night];
it seems less of an accident when you notice that he names all four.”
<a href="https://archive.org/details/criticsnotebook0000unse/page/152">A. E. Housman</a>, December 1928:
“The silver sphere is the Morning Star … The <em>moon</em>, when her
intense lamp narrows in the white dawn clear, is not a sphere but a sickle: when
she is a sphere at sunrise she is near the western horizon, visible in broad daylight
and disappearing only when she sets; so that nothing could be <em>less</em> like the vanishing
of the skylark.” And yet Shelley certainly does compare the skylark to the moon, also.</p>
  </li>
  <li>
    <p>Irrelevancy: the reader must invent interpretations.
Yeats’ “Fergus and the Druid” tells how King Fergus renounced
his active life — “A wild and foolish labourer is a king — to do, and do, and do, and
never dream … I would be no more a king, but learn the dreaming wisdom that is yours” —
yet, upon gaining his desire, “now I have grown nothing, being all, / And the whole
world weighs down upon my heart.” 
Empson considers Yeats’ followup poem: “Who will go drive with Fergus now /
And pierce the deep wood’s woven shade?” Is this poem set <em>before</em> Fergus’ transformation,
making it a call for volunteers (“There is still time to drive with Fergus, as he is still
a king in the world”)? Or is this poem set <em>after</em>, making it an
<a href="https://en.wikipedia.org/wiki/Ubi_sunt"><em>ubi sunt?</em></a>
When it is said, irrelevantly, that Fergus <em>now</em> “rules the shadows of the wood, and the dishevelled wandering stars,”
is this meant to be comforting (rules <em>even</em>) or disquieting (rules <em>only</em>)?</p>
  </li>
  <li>
    <p>“Full contradiction.”
Pope’s “A mighty maze, but not without a plan” gives occasion to point out that 
“a <em>maze</em> is conceived as something that at once has and has not got a <em>plan</em>.”
Many “muddles with negatives” such as Spenser’s “But stings and sharpest steele did
far exceed / The sharpnesse of his cruell rending clawes,” and
<a href="https://www.folger.edu/explore/shakespeares-works/coriolanus/read/1/4/">Shakespeare’s</a>
“No, nor a man that fears you less than he: that’s lesser than a little.”</p>
  </li>
</ol>

<hr />

<p>A small collection of quotable quotes from <em>Seven Types of Ambiguity:</em></p>

<p><a href="https://archive.org/details/seventypesofambi00empsrich/page/7">Page 7</a>:</p>

<blockquote>
  <p>There is much danger of triviality in this, because it requires a display
of ingenuity such as can easily be used to escape from the consciousness
of one’s ignorance […]</p>
</blockquote>

<p><a href="https://archive.org/details/seventypesofambi00empsrich/page/25">Page 25</a>:</p>

<blockquote>
  <p>All languages are composed of dead metaphors, as the soil of corpses;
but English is perhaps uniquely full of metaphors of this sort, which are
not dead but sleeping, and, while making a direct statement, color it with
an implied comparison. The school rule against mixed metaphor, which in
itself is so powerful a weapon, is largely necessary because of the
presence of these sleepers, who must be treated with respect.</p>
</blockquote>

<p><a href="https://archive.org/details/seventypesofambi00empsrich/page/106">Page 106</a>:</p>

<blockquote>
  <p>[As to puns, Marvell] manages to feel Elizabethan about them, to imply
that it was quite easy to produce puns and one need not worry about one’s dignity
in the matter. It became harder as the language was tidied up, and one’s dignity
was more seriously engaged. For the Elizabethans were quite prepared, for instance,
to make a pun by a mispronunciation, would treat puns as mere casual bricks,
requiring no great refinement, of which any number could easily be collected
for a flirtation or an indignant harangue. By the time English had become anxious
to be “correct” the great thing about a pun was that it was not a Bad Pun, that
it satisfied the Unities and what not; it could stand alone and would expect
admiration, and was a much more elegant affair.</p>
</blockquote>

<p><a href="https://archive.org/details/seventypesofambi00empsrich/page/158">Page 158</a>:</p>

<blockquote>
  <p>One of the basic assumptions of Shelley’s poetry is that the poet stands in a
very peculiar relation to ordinary people; he is an outcast and an unacknowledged
legislator, and probably dying as well.</p>
</blockquote>

<p><a href="https://archive.org/details/seventypesofambi00empsrich/page/165">Page 165</a>:</p>

<blockquote>
  <p>Later English poetry [such as Swinburne] is full of subdued conceits and ambiguities,
in the sense that a reader has to know what the pun which establishes a connection
would have been if it had been made, or has to be accustomed to conceits in poetry,
so that, though a conceit has not actually been worked out, he can feel it as
fundamental material, as the justification of an apparent disorder. In the same way
such poetry will often imply a direction of thought, or connection of ideas, by a
transition from one sleeping metaphor to another. Later nineteenth-century poetry
carried this delicacy to such a degree that it can reasonably be called decadent,
because its effects depended on a tradition that its example was destroying.</p>
</blockquote>]]></content><author><name></name></author><category term="litclub" /><category term="old-shit" /><category term="poetry" /><summary type="html"><![CDATA[The other week I read William Empson’s Seven Types of Ambiguity (first published 1930, third edition 1953 with new footnotes by the author). Empson purports to taxonomize all sorts of “ambiguity,” defined as any technique or nuance “which gives room for alternative reactions to the same piece of language.” Really I’m not sure I fully grasped his taxonomy. (One might call it a taxonomy of ambiguity in more ways than one!) But it’s something like this:]]></summary></entry><entry><title type="html">MSVC’s `/experimental:constevalVfuncNoVtable` is non-conforming</title><link href="https://quuxplusone.github.io/blog/2026/03/12/consteval-vfunc-no-vtable/" rel="alternate" type="text/html" title="MSVC’s `/experimental:constevalVfuncNoVtable` is non-conforming" /><published>2026-03-12T00:01:00+00:00</published><updated>2026-03-12T00:01:00+00:00</updated><id>https://quuxplusone.github.io/blog/2026/03/12/consteval-vfunc-no-vtable</id><content type="html" xml:base="https://quuxplusone.github.io/blog/2026/03/12/consteval-vfunc-no-vtable/"><![CDATA[<p><a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1064r0.html#_proposed_changes">P1064 “Allowing Virtual Function Calls in Constant Expressions,”</a>
adopted for C++20, permits you to mark virtual functions as <code class="language-plaintext highlighter-rouge">constexpr</code> and
then call them at compile-time. In fact, you can even mark them <code class="language-plaintext highlighter-rouge">consteval</code>
(also a C++20 feature), which means you can call them <em>only</em> at compile-time.
Thus (<a href="https://godbolt.org/z/1Eo4Edde4">Godbolt</a>):</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>struct Base {
  consteval virtual int f() const { return 1; }
};
struct Derived : Base {
  consteval int f() const override { return 2; }
};

constexpr const Base *m() { static constexpr Derived d; return &amp;d; }

static_assert(std::is_polymorphic_v&lt;Base&gt;);
static_assert(std::is_polymorphic_v&lt;Derived&gt;);
static_assert(sizeof(Base) == 8);
static_assert(sizeof(Derived) == 8);
static_assert(typeid(*m()) == typeid(Derived));
static_assert(m()-&gt;f() == 2);
</code></pre></div></div>

<p>None of the <code class="language-plaintext highlighter-rouge">static_assert</code>s above should be surprising. <code class="language-plaintext highlighter-rouge">Base</code> is certainly
<a href="https://eel.is/c++draft/class.virtual#def:class,polymorphic">polymorphic</a>
(it has virtual methods); the result of <code class="language-plaintext highlighter-rouge">typeid</code> and the behavior of <code class="language-plaintext highlighter-rouge">m()-&gt;f()</code> are mandated
by the Standard; and <code class="language-plaintext highlighter-rouge">sizeof(Base)</code> is 8 because of its 8-byte vptr. Now,
<code class="language-plaintext highlighter-rouge">Base</code>’s vptr is really useful only at compile time,
because <code class="language-plaintext highlighter-rouge">f</code>, the only virtual function in <code class="language-plaintext highlighter-rouge">Base</code>’s vtable, is callable only at
compile time. So you might think we could somehow save some space by giving
<code class="language-plaintext highlighter-rouge">Base</code> a “<code class="language-plaintext highlighter-rouge">consteval</code>-only vptr” — omitting the vptr from its struct layout at runtime.
However, there are several problems with that idea.</p>

<p>First: Can we give <code class="language-plaintext highlighter-rouge">Base</code> two different layouts — include the vptr in the constexpr-time layout
and omit it from the runtime layout? No, because that would make <code class="language-plaintext highlighter-rouge">sizeof</code> give two
different values depending on whether the expression <code class="language-plaintext highlighter-rouge">sizeof(Base)</code> was seen in a
<a href="https://stackoverflow.com/questions/65429853/how-to-understand-the-definition-of-manifestly-constant-evaluated">manifestly constant-evaluated</a>
context. That would be a nightmare.</p>

<p>Second: If we omit <code class="language-plaintext highlighter-rouge">Base</code>’s vptr at compile time <em>and</em> at runtime, (A) how would that affect
its type traits? and (B) where would we hang its runtime type information?</p>

<p>Today I learned that MSVC 16.11 (way back in 2021) added an experimental switch
to see what happens if you answer those questions the “wrong” way. The switch is named
<a href="https://devblogs.microsoft.com/cppblog/msvc-cpp20-and-the-std-cpp20-switch/#iso-c++20-continuing-work-defect-reports-and-clarifications"><code class="language-plaintext highlighter-rouge">/experimental:constevalVfuncNoVtable</code></a>.
If you turn it on, you’ll observe the following surprising behavior
(<a href="https://godbolt.org/z/96dr8xh41">Godbolt</a>):</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>static_assert(!std::is_polymorphic_v&lt;Base&gt;);
static_assert(!std::is_polymorphic_v&lt;Derived&gt;);
static_assert(sizeof(Base) == 1);
static_assert(sizeof(Derived) == 1);
static_assert(typeid(*m()) == typeid(Base));
static_assert(m()-&gt;f() == 2);
</code></pre></div></div>

<p>In this experimental mode, MSVC omits <code class="language-plaintext highlighter-rouge">Base</code>’s vptr — changing its <code class="language-plaintext highlighter-rouge">sizeof</code> at both compile time and
runtime. Yet <code class="language-plaintext highlighter-rouge">m()-&gt;f()</code> continues to perform virtual dispatch and call <code class="language-plaintext highlighter-rouge">Derived::f()</code>! (The compiler’s
built-in constexpr interpreter knows the true dynamic type of every object created at constexpr time,
so this is easy for it. And you can’t call <code class="language-plaintext highlighter-rouge">f</code> at runtime.)</p>

<p>In this mode, MSVC decides that <code class="language-plaintext highlighter-rouge">Base</code> is actually non-polymorphic (despite the Standard’s clearly stating
that it is: it has virtual methods). Having discarded polymorphic-ness, MSVC also classifies <code class="language-plaintext highlighter-rouge">Base</code> as
<a href="https://eel.is/c++draft/tab:meta.unary.prop">empty</a> and trivially copyable; furthermore,
<code class="language-plaintext highlighter-rouge">typeid(*m())</code> can skip the evaluation of <code class="language-plaintext highlighter-rouge">*m()</code> (because such evaluation
is required only for glvalue expressions of <em>polymorphic</em> type) and invariably return <code class="language-plaintext highlighter-rouge">typeid(Base)</code>.</p>

<p>Could MSVC make this mode’s behavior more conforming by patching their type-traits to report that
<code class="language-plaintext highlighter-rouge">Base</code> <em>is</em> polymorphic (and non-empty, and non-trivially copyable), and making <code class="language-plaintext highlighter-rouge">typeid</code> report the correct
dynamic type via the same compiler magic that powers the dynamic dispatch in <code class="language-plaintext highlighter-rouge">m()-&gt;f()</code>?
Almost, but not quite. They could use compiler magic to make <code class="language-plaintext highlighter-rouge">typeid</code> work polymorphically at constexpr time;
but without a vtable to hang the RTTI on, <code class="language-plaintext highlighter-rouge">typeid</code> would certainly <em>have</em> to act non-polymorphically at runtime,
<a href="https://godbolt.org/z/184vM8K5s">as seen here</a>.</p>

<p>Conclusion: MSVC’s five-year-old <code class="language-plaintext highlighter-rouge">/experimental:constevalVfuncNoVtable</code> gives you a non-conforming
experience, in which types with all-<code class="language-plaintext highlighter-rouge">consteval</code> virtual functions are treated as non-polymorphic.
Its opposite, <code class="language-plaintext highlighter-rouge">/experimental:constevalVfuncVtable</code>, gives conforming behavior that matches GCC and Clang
(as far as I know).</p>]]></content><author><name></name></author><category term="abi" /><category term="constexpr" /><category term="classical-polymorphism" /><category term="cpplang-slack" /><category term="msvc" /><category term="today-i-learned" /><summary type="html"><![CDATA[P1064 “Allowing Virtual Function Calls in Constant Expressions,” adopted for C++20, permits you to mark virtual functions as constexpr and then call them at compile-time. In fact, you can even mark them consteval (also a C++20 feature), which means you can call them only at compile-time. Thus (Godbolt):]]></summary></entry><entry><title type="html">A dialogue on trivial-abi and trivially relocatable</title><link href="https://quuxplusone.github.io/blog/2026/03/02/trivially-relocatable-vs-trivial-abi/" rel="alternate" type="text/html" title="A dialogue on trivial-abi and trivially relocatable" /><published>2026-03-02T00:01:00+00:00</published><updated>2026-03-02T00:01:00+00:00</updated><id>https://quuxplusone.github.io/blog/2026/03/02/trivially-relocatable-vs-trivial-abi</id><content type="html" xml:base="https://quuxplusone.github.io/blog/2026/03/02/trivially-relocatable-vs-trivial-abi/"><![CDATA[<p>I wrote in <a href="/blog/2018/05/02/trivial-abi-101/">”<code class="language-plaintext highlighter-rouge">[[trivial_abi]]</code> 101”</a> (May 2018),
during the active development of the <code class="language-plaintext highlighter-rouge">[[clang::trivial_abi]]</code> attribute:</p>

<blockquote>
  <p><b>Relation to trivial relocatability:</b> None… well, some?</p>

  <p>As you can see, there is no requirement that a <code class="language-plaintext highlighter-rouge">[[trivial_abi]]</code> class type
should have any particular semantics for its move constructor, its destructor,
or its default constructor. Any given class type will <em>likely</em> be trivially
relocatable, simply because most class types are trivially relocatable by accident…</p>
</blockquote>

<p>A correspondent writes in with a query for Socrates
(previously seen on this blog <a href="/blog/2023/07/16/why-span-has-no-resize/">in July 2023</a>).</p>

<p><em>Epistolographos writes:</em> Actually, I think trivial-abi-ness and trivial relocatability are
almost the same thing. Let me quote <a href="/blog/2018/05/02/trivial-abi-101/#the-intuition-here-is-that-when">your own words</a>
back to you:</p>

<blockquote>
  <p>When a thing is trivial-abi, any time you expect <em>copy</em> you might actually get <em>copy-plus-<code class="language-plaintext highlighter-rouge">memcpy</code>:</em>
The “put it in a register and then take it back out” operation is essentially tantamount to <code class="language-plaintext highlighter-rouge">memcpy</code>.
And similarly when you expect <em>move</em> you might actually get <em>move-plus-<code class="language-plaintext highlighter-rouge">memcpy</code>.</em></p>

  <p>Contrariwise, when a thing is trivially relocatable, any time you expect <em>copy-plus-destroy</em>
you might actually get <em><code class="language-plaintext highlighter-rouge">memcpy</code></em>. And similarly when you expect <em>move-plus-destroy</em>
you might actually get <em><code class="language-plaintext highlighter-rouge">memcpy</code></em>. You actually <em>lose</em> calls to special member functions
when you’re talking about “trivial relocation”; whereas with trivial-abi you never lose
calls — you just get (as if) <code class="language-plaintext highlighter-rouge">memcpy</code> in addition to the calls you expected.</p>
</blockquote>

<p>I (said Epistolographos) want to argue that passing a trivial-abi parameter <em>also</em> replaces
move-and-destroy with <code class="language-plaintext highlighter-rouge">memcpy</code>. The way I see it, passing a trivial-abi argument doesn’t just
move that argument’s destruction into the callee; it conceptually introduces additional moves and
destroys, which are then folded back out. Suppose I write this in my source code:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>struct [[clang::trivial_abi]] T {};

T produce();
void consume(T arg) {
  use(arg);
}
int main() {
  consume(produce());
}
</code></pre></div></div>

<p>The compiler (said Epistolographos) will essentially rewrite this into:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>struct [[clang::trivial_abi]] T {};

T produce();
void consume(T inreg) {
  T arg = std::move(inreg); // and destroy inreg early
  use(arg);
}
int main() {
  T onstack = produce();
  consume(std::move(onstack)); // and destroy onstack early
}
</code></pre></div></div>

<p>Each commented line does an extra move-and-destroy — that’s a relocate. Now, the compiler doesn’t
actually generate calls to the move-constructor and destructor there; instead, it codegens
those lines by copying the bits of the object into a register (to relocate from <code class="language-plaintext highlighter-rouge">onstack</code> into <code class="language-plaintext highlighter-rouge">inreg</code>)
and then copying the bits back to the stack (to relocate from <code class="language-plaintext highlighter-rouge">inreg</code> into <code class="language-plaintext highlighter-rouge">arg</code>). Since the
compiler is replacing a relocation with a bitwise copy, I deduce that <code class="language-plaintext highlighter-rouge">T</code> must be a
trivially relocatable type.</p>

<p>— I think I see your point (said Socrates). If the compiler rewrites move-and-destroy into
a copy of <code class="language-plaintext highlighter-rouge">T</code>’s object representation, we should indeed deduce that the compiler thinks <code class="language-plaintext highlighter-rouge">T</code>
is trivially relocatable. But surely your argument is a little weak at two points: First,
how can we be certain that there <em>is</em> a move-and-destroy happening? And second, even if
the compiler thinks <code class="language-plaintext highlighter-rouge">T</code> is trivially relocatable, is that something it <em>knows</em> or merely
something it <em>assumes?</em></p>

<p>Usually, to see whether a move-and-destroy is really happening, we would
instrument our type’s special member functions, like this (<a href="https://godbolt.org/z/GjarPGPW1">Godbolt</a>):</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>int count = 0;
struct [[clang::trivial_abi]] Counter {
  explicit Counter() { ++count; }
  Counter(const Counter&amp;) { ++count; }
  void operator=(const Counter&amp;) {}
  ~Counter() { --count; }
};
Counter produce() {
  Counter c;
  assert(count == 1);
  return c;
}
void consume(Counter arg) {
  assert(count == 1);
}
int main() {
  assert(count == 0);
  consume(produce());
  assert(count == 0);
}
</code></pre></div></div>

<p>This test passes, no matter whether we use the <code class="language-plaintext highlighter-rouge">[[clang::trivial_abi]]</code> attribute or not.
That’s because <code class="language-plaintext highlighter-rouge">count</code> counts the number of extant objects: relocating an object
from one place to another doesn’t change the number of extant objects. So it is correct
to describe this <code class="language-plaintext highlighter-rouge">Counter</code> type as <em>trivially</em> relocatable: we can replace its relocation
operation with <code class="language-plaintext highlighter-rouge">memcpy</code> and the behavior of the program doesn’t change.</p>

<p>— Exactly! (exclaimed Epistolographos). And look here: When I ask Clang whether <code class="language-plaintext highlighter-rouge">Counter</code>
is trivially relocatable, it gives me the right answer!</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>static_assert(__is_trivially_relocatable(Counter));
  // passes when Counter is [[clang::trivial_abi]]
</code></pre></div></div>

<p>— Yes, Epistolographos; but the same <code class="language-plaintext highlighter-rouge">static_assert</code> fails when we remove <code class="language-plaintext highlighter-rouge">[[clang::trivial_abi]]</code>
from the class definition. Yet the behavior of <code class="language-plaintext highlighter-rouge">Counter</code> hasn’t changed. How can this be?</p>

<p>— Well, Socrates, of course the non-trivial-abi <code class="language-plaintext highlighter-rouge">Counter</code> remains “Platonically” trivially
relocatable (to coin an anachronism). But without the attribute, the compiler doesn’t <em>know</em>
<code class="language-plaintext highlighter-rouge">Counter</code> to be trivially relocatable. The <code class="language-plaintext highlighter-rouge">[[clang::trivial_abi]]</code> attribute gives the
compiler this special knowledge, in the same way that P1144’s <code class="language-plaintext highlighter-rouge">[[trivially_relocatable]]</code>
attribute gives the compiler special knowledge it didn’t have before.</p>

<p>— Knowledge is good, certainly. But how can we be sure that this <em>is</em> knowledge, and not
merely opinion? For example, suppose I change <code class="language-plaintext highlighter-rouge">Counter</code> to tally the number of constructor
calls, instead of the number of extant objects. I’ll call this version <code class="language-plaintext highlighter-rouge">UpCounter</code>
(<a href="https://godbolt.org/z/Y9Pfxsf3W">Godbolt</a>):</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>int count = 0;
struct UpCounter {
  explicit UpCounter(int) { ++count; }
  UpCounter(const UpCounter&amp;) { ++count; }
  void operator=(const UpCounter&amp;) {}
  ~UpCounter() {} // here is the difference
};
UpCounter produce() {
  auto c = UpCounter(42);
  assert(count == 1); // A
  return c;
}
void consume(UpCounter arg) {
  assert(count == 1); // B
}
int main() {
  assert(count == 0);
  consume(produce());
  assert(count == 1); // C
}
</code></pre></div></div>

<p>You’d agree that this program’s behavior is fully specified by the Standard?</p>

<p>— Yes, Socrates, except for the use of NRVO in <code class="language-plaintext highlighter-rouge">produce</code>. That line is permitted to move-construct
from <code class="language-plaintext highlighter-rouge">c</code>, even though no reasonable compiler would do so.</p>

<p>— And if that line did move-construct from <code class="language-plaintext highlighter-rouge">c</code>, we’d expect <code class="language-plaintext highlighter-rouge">count</code> to equal <code class="language-plaintext highlighter-rouge">2</code> on lines B and C?</p>

<p>— Yes, Socrates.</p>

<p>— Even though that additional move-construction would be balanced out by an additional destruction?</p>

<p>— Yes, Socrates, because <code class="language-plaintext highlighter-rouge">UpCounter</code> increments <code class="language-plaintext highlighter-rouge">count</code> in every constructor, and never decrements it.
So every move-and-destroy operation increments <code class="language-plaintext highlighter-rouge">count</code> by one.</p>

<p>— For that reason, is <code class="language-plaintext highlighter-rouge">UpCounter</code> trivially relocatable?</p>

<p>— No, Socrates, because <code class="language-plaintext highlighter-rouge">UpCounter</code>’s move-and-destroy, unlike our original <code class="language-plaintext highlighter-rouge">Counter</code>’s, is visibly
different from a simple <code class="language-plaintext highlighter-rouge">memcpy</code>. Clang knows this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>static_assert(!__is_trivially_relocatable(UpCounter));
  // passes as long as UpCounter is not [[clang::trivial_abi]]
</code></pre></div></div>

<p>— But look here, Epistolographos: I apply the <code class="language-plaintext highlighter-rouge">[[clang::trivial_abi]]</code> attribute to <code class="language-plaintext highlighter-rouge">UpCounter</code>…</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>struct [[clang::trivial_abi]] UpCounter {
  explicit UpCounter(int) { ++count; }
  UpCounter(const UpCounter&amp;) { ++count; }
  void operator=(const UpCounter&amp;) {}
  ~UpCounter() {}
};
static_assert(__is_trivially_relocatable(UpCounter));
</code></pre></div></div>

<p>…and suddenly Clang thinks that it <em>is</em> trivially relocatable.
Yet the behavior of <code class="language-plaintext highlighter-rouge">UpCounter</code> hasn’t changed! How can this be?</p>

<p>— This case gives me pause, Socrates. It seems to me that <code class="language-plaintext highlighter-rouge">UpCounter</code> is not “Platonically”
trivially relocatable. Yet applying the attribute causes Clang to opine that it <em>is</em> trivially relocatable.</p>

<p>— Now, according to my theory, declaring a type trivial-abi causes the compiler to generate
many additional bitwise copies. According to <em>your</em> theory, it causes the compiler to generate
many additional move-and-destroy cycles and then inerrantly replace all of them with bitwise copies.
Either way, we agree that the compiler generates many bitwise copies. So, if I take a class like
<code class="language-plaintext highlighter-rouge">offset_ptr</code> and mark it trivial-abi, <a href="/blog/2018/05/02/trivial-abi-101/#we-can-easily-design-an-offset_p">it will not work</a>.
Or if I take a libstdc++-style <code class="language-plaintext highlighter-rouge">string</code> implementation (<a href="https://godbolt.org/z/dznnK3zY3">Godbolt</a>,
<a href="/blog/code/2026-03-02-trivial-abi-string.cpp">backup</a>) and mark it trivial-abi, it won’t work
either.</p>

<p>— That’s right. It simply makes no sense to mark a non-trivially-relocatable class
trivial-abi. Doing that would be wrong.</p>

<p>— Here we agree. Recall the theory of attributes as warrants: a warrant is a way for the programmer,
who may be assumed to know what he’s doing, to communicate Platonic Truth to the compiler.
As my friend Ko-Ko once put it: The programmer says “Make this type trivial-abi,” and this type is
told off to be bitwise-copied. Consequently that type is as good as trivially relocated — practically,
it <em>is</em> trivially relocated — and if it <em>is</em> trivially relocated… why not say so?</p>

<p>Now, when my friend said that, he said it rather weakly, with a plaintive look; but in this instance
the sentiment is defensible. Since the calling convention for trivial-abi types <em>requires</em> that a type be immune
to bitwise relocations, it seems like a plain and simple error to mark any non-trivially-relocatable type
<code class="language-plaintext highlighter-rouge">[[clang::trivial_abi]]</code>. It is an old C++ custom to treat “plain and simple errors” as “UB” — to
pretend they never happen — and thus it is perfectly reasonable for Clang to opine that every type
marked with <code class="language-plaintext highlighter-rouge">[[clang::trivial_abi]]</code> is in fact trivially relocatable.</p>

<p>— That’s what I’m saying!</p>

<p>— But, Epistolographos, this happens only because the Clang maintainers decided (in early 2022,
via <a href="https://reviews.llvm.org/D114732">D114732</a>) to encode that opinion into Clang.
(See <a href="https://reviews.llvm.org/D114732?id=390435#inline-1097618">this comment thread</a> in particular.)</p>

<p>— Still, it’s exactly as I said: Every type marked with <code class="language-plaintext highlighter-rouge">[[clang::trivial_abi]]</code> is
trivially relocatable!</p>

<p>— Well, not quite. Consider this type <code class="language-plaintext highlighter-rouge">A&lt;N&gt;</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>struct N { ~N(); };
template&lt;class T&gt;
struct [[clang::trivial_abi]] A { T t; };
static_assert(!__is_trivially_relocatable(A&lt;N&gt;));
</code></pre></div></div>

<p>— All right, Socrates; that’s because <code class="language-plaintext highlighter-rouge">[[clang::trivial_abi]]</code> behaves more like what you call
<code class="language-plaintext highlighter-rouge">[[maybe_trivially_relocatable]]</code>, with “dull-knife” rather than “sharp-knife” semantics.
[<em>“In <a href="https://github.com/Quuxplusone/llvm-project/issues/52">almost</a> all cases,” interjected Socrates.</em>]
In this case, because <code class="language-plaintext highlighter-rouge">N</code> is non-trivial-abi, <code class="language-plaintext highlighter-rouge">A&lt;N&gt;</code> quietly discards the attribute.
Let me rephrase: Every type which is
<a href="https://itanium-cxx-abi.github.io/cxx-abi/abi.html#:~:text=trivial%20for%20the%20purposes%20of%20calls">trivial for the purposes of calls</a>
is trivially relocatable.</p>

<p>— Well, not quite. Consider a type that is <em>not</em> marked with the attribute, but which is
trivial for the purposes of calls because it has defaulted trivial copy/move constructors and
a defaulted trivial destructor. Now, give that type a non-defaulted <code class="language-plaintext highlighter-rouge">operator=</code>. Now it’s
non-trivially relocatable; but it remains trivial for the purposes of calls, because the
calling convention doesn’t care about <code class="language-plaintext highlighter-rouge">operator=</code>.</p>

<p>— Ah, right. When we say a type is “trivial-abi,” we’re talking about how it interacts with the calling convention.</p>

<p>— Well, not quite. Consider <code class="language-plaintext highlighter-rouge">std::array&lt;int, 100&gt;</code>. It’s trivial for the purposes of calls,
and so I think I would say that it is “trivial-abi”; but it is certainly not passed or returned
any differently from, say, <code class="language-plaintext highlighter-rouge">std::string</code>.</p>

<p>But I agree that in general, “trivial-abi” pertains to the calling convention, while “trivially relocatable”
pertains to library behavior. For example, it’s very easy to think of types that are trivially relocatable
but not trivial-abi; <code class="language-plaintext highlighter-rouge">unique_ptr</code> is such a type.</p>

<p>— libc++ has an extension to make <code class="language-plaintext highlighter-rouge">unique_ptr</code> trivial-abi, doesn’t it?</p>

<p>— Yes, it does. (<a href="https://godbolt.org/z/vhMnzs89q">Godbolt.</a>) The convention by which <code class="language-plaintext highlighter-rouge">unique_ptr</code>
is passed and returned is controlled by
<span style="white-space: nowrap;"><code class="language-plaintext highlighter-rouge">-D_LIBCPP_ABI_ENABLE_UNIQUE_PTR_TRIVIAL_ABI</code>.</span>
This is completely orthogonal to the mechanism by which libc++ recognizes <code class="language-plaintext highlighter-rouge">unique_ptr</code>
as one of those trivially relocatable types for which e.g. <code class="language-plaintext highlighter-rouge">vector</code> reallocation ought
to use <code class="language-plaintext highlighter-rouge">memcpy</code> instead of move-and-destroy. Notice, in that Godbolt, how the compilations
of <code class="language-plaintext highlighter-rouge">demonstrate_calling_convention</code> differ, but both compilations successfully use <code class="language-plaintext highlighter-rouge">memcpy</code>
in <code class="language-plaintext highlighter-rouge">reallocate_vector</code>.</p>

<p>— So, to sum up:
Not all trivial-abi types are passed in registers; but whenever a trivial-abi type <em>is</em> passed in registers,
it’s constructed in one place and destroyed in another: effectively, it is trivially relocated.
Putting the <code class="language-plaintext highlighter-rouge">[[clang::trivial_abi]]</code> attribute on a class doesn’t necessarily mean Clang
<em>will</em> make that class trivial-abi, let alone that it will be passed in registers; but the programmer
would be foolish to put the attribute on any class that couldn’t survive trivial relocation.
Clang <em>opines</em> that the programmer is not foolish; therefore, Clang opines that any class marked
<code class="language-plaintext highlighter-rouge">[[clang::trivial_abi]]</code> should be reported as <code class="language-plaintext highlighter-rouge">__is_trivially_relocatable</code>.</p>

<p>— Yes, Epistolographos, I find no fault with your summary. I would add two things:
First, Clang is quite pragmatic to make that assumption. Good for Clang. And second, you must remember
that the implication goes only forward, not backward. There are very many class types which are
trivially relocatable but not trivial-abi.</p>

<p>— How so? Isn’t it true that any trivially relocatable type can be relocated from one place to another
as-if by copying its bits into a register and out again?</p>

<p>— Yes, that’s true.</p>

<p>— So marking a trivially relocatable class type as <code class="language-plaintext highlighter-rouge">[[clang::trivial_abi]]</code> must always be safe!
I think I should start using <code class="language-plaintext highlighter-rouge">[[clang::trivial_abi]]</code> as a poor man’s version of P1144 <code class="language-plaintext highlighter-rouge">[[trivially_relocatable]]</code>:
I’ll just put it on every class type that I want Clang to recognize as <code class="language-plaintext highlighter-rouge">__is_trivially_relocatable</code>!</p>

<p>— No, Epistolographos, that’ll bite you in practice, for at least two reasons. First, and most importantly, “being trivial-abi”
<em>has ABI implications.</em> Remember, the libc++ maintainers know that <code class="language-plaintext highlighter-rouge">unique_ptr</code> is trivially relocatable,
but they didn’t just unilaterally mark it as <code class="language-plaintext highlighter-rouge">[[clang::trivial_abi]]</code>, because that changes the calling convention
for all of its users. If you try to link one TU compiled with
<span style="white-space: nowrap;"><code class="language-plaintext highlighter-rouge">-D_LIBCPP_ABI_ENABLE_UNIQUE_PTR_TRIVIAL_ABI</code></span> against
another TU compiled without it, you’ll get linker errors at best and runtime UB at worst: one side will pass
<code class="language-plaintext highlighter-rouge">unique_ptr</code> arguments on the stack when the other expects them in registers, and vice versa.
Contrariwise, annotating a type with the “real” <code class="language-plaintext highlighter-rouge">[[trivially_relocatable]]</code> attribute doesn’t change its ABI at all.
Many real-world codebases require a way to warrant a type as trivially relocatable without changing its ABI,
so your idea of using <code class="language-plaintext highlighter-rouge">[[clang::trivial_abi]]</code> to mean “trivially-relocatable” isn’t directly useful to them.</p>

<p>— What’s the second reason?</p>

<p>— Secondly, as we mentioned above, <code class="language-plaintext highlighter-rouge">[[clang::trivial_abi]]</code> has “dull-knife” semantics.
You can’t use that attribute to warrant that, say, the <code class="language-plaintext highlighter-rouge">boost::shared_ptr</code> you’re using
is <em>in fact</em> trivially relocatable. (You wouldn’t want to, because of the ABI implications; but also, you couldn’t.)
Most real-world codebases require a way to warrant a type as trivially relocatable without “virally” annotating
all its data members’ types; in that sense <code class="language-plaintext highlighter-rouge">[[clang::trivial_abi]]</code> doesn’t serve at all as a “poor man’s <code class="language-plaintext highlighter-rouge">[[trivially_relocatable]]</code>.”</p>

<p>And that’s fine: <code class="language-plaintext highlighter-rouge">[[clang::trivial_abi]]</code> doesn’t exist to be a poor man’s version of anything else.
It exists to change a type’s ABI — hence its name! You should use it only for that purpose. The fact that
it also happens (by deliberate convention) to imply trivial relocatability is a mere lagniappe.</p>

<p>— I see. Well, good day, Socrates.</p>

<p>— Good day, Epistolographos.</p>]]></content><author><name></name></author><category term="abi" /><category term="library-design" /><category term="relocatability" /><category term="socrates" /><category term="triviality" /><summary type="html"><![CDATA[I wrote in ["`[[trivial_abi]]` 101"](/blog/2018/05/02/trivial-abi-101/) (May 2018), during the active development of the `[[clang::trivial_abi]]` attribute: > Relation to trivial relocatability: None... well, some? > > As you can see, there is no requirement that a `[[trivial_abi]]` class type > should have any particular semantics for its move constructor, its destructor, > or its default constructor. Any given class type will _likely_ be trivially > relocatable, simply because most class types are trivially relocatable by accident... A correspondent writes in with a query for Socrates (previously seen on this blog [in July 2023](/blog/2023/07/16/why-span-has-no-resize/)). _Epistolographos writes:_ Actually, I think trivial-abi-ness and trivial relocatability are almost the same thing.]]></summary></entry><entry><title type="html">What I’m reading lately: Graves on caves</title><link href="https://quuxplusone.github.io/blog/2026/02/15/the-caves-of-arta/" rel="alternate" type="text/html" title="What I’m reading lately: Graves on caves" /><published>2026-02-15T00:01:00+00:00</published><updated>2026-02-15T00:01:00+00:00</updated><id>https://quuxplusone.github.io/blog/2026/02/15/the-caves-of-arta</id><content type="html" xml:base="https://quuxplusone.github.io/blog/2026/02/15/the-caves-of-arta/"><![CDATA[<p>I recently read, and enjoyed, David Owen’s
<a href="https://archive.is/JqOpO">“The Objectively Objectionable Grammatical Pet Peeve”</a>
(January 2023). The objectionable practice in question is what Fowler
<a href="https://archive.org/details/in.ernet.dli.2015.65263/page/423">calls</a>
the “sentry participle,” a participle or appositive stuck on the front of
a sentence that could just as well have done without it. For example:</p>

<blockquote>
  <p>Known affectionately as “the girls,”
Ruth and Emily have a lot of fun for two Asian elephants.</p>
</blockquote>

<p>Often associated with newspaper obituaries and puff pieces, the practice is
(Owen says) relatively new to English. Er, ahem: Owen says this particular practice
is relatively new to English, having gained currency from the kind of “journalese”
seen in newspaper obituaries and puff pieces only since the early 20th century.</p>

<p>I hypothesize (or admit) that the use of sentry participles might be a symptom
of the writer’s failing to think out the whole sentence before starting to write.
Instead of thinking in full sentences, you can start with just a participial phrase —
the kind of thing you’d see in a bulleted list on a PowerPoint slide — and defer
for a few seconds the unpleasant task of turning that bullet point into a full sentence.</p>

<blockquote>
  <p>The awkwardness is obvious if you imagine hearing one in conversation.
No one has ever said to you, “A sophomore at Cornell, my niece is coming home for Christmas.”
[…] Yet if you were, let’s say, writing an obituary for your college’s alumni magazine,
you wouldn’t hesitate: “A standout schoolboy athlete, he ran his family’s
door-and-window business.”</p>
</blockquote>

<hr />

<p>Today, via Language Log posts on <a href="https://languagehat.com/the-history-of-the-limerick/">the purported etymology of “limerick”</a> (April 2017)
and <a href="https://languagehat.com/orthographical-limericks/">orthographical limericks</a> (August 2008),
and <a href="http://www.users.zetnet.co.uk/bywater/ee_res9a.htm">this circa-2000 time capsule of the Old Web</a>:</p>

<blockquote>
  <p><a href="https://en.wikipedia.org/wiki/Norman_St_John-Stevas">The Baron of Fawsley</a>, Lord St John,<br />
Had a fine buckskin coat with a frt john.<br />
He said, “It was guthven<br />
Me by <a href="https://en.wikipedia.org/wiki/Grey_Gowrie">Viscount Ruthven</a>—<br />
Who must think I’m a cowboy, or t john.”</p>
</blockquote>

<p>The same page introduced me to the following bit of doggerel from Robert Graves’
(yes, <a href="https://en.wikipedia.org/wiki/Robert_Graves"><em>that</em></a> Robert Graves’)
otherwise inutile 1951 collection <a href="https://archive.org/details/poemssatires0000rgra/page/31"><em>Poems and Satires</em></a>.
The following, which reminds me of Edgar Allan Poe (in the way it combines
<a href="https://en.wikipedia.org/wiki/A_Predicament">“A Predicament”</a> ’s addlepated excess with
<a href="https://www.eapoe.org/works/stedwood/sw0208.htm">“The Domain of Arnheim”</a> ’s kublakhanity)
is printed under the title “¡Wellcome, to the Caves of Arta!”</p>

<blockquote>
  <p><i>“They are hollowed out in the see coast at the muncipal terminal of
Capdepera, at nine kilometer from the town of Arta in the Island of
Mallorca, with a suporizing infinity of graceful colums of 21 meter
and by downward, which prives the spectator of all animacion and
plunges in dumbness. The way going is very picturesque, serpentine
between style mountains, til the arrival at the esplanade of the vallee
called «The Spiders». There are good enlacements of the railroad with
autobuses of excursion, many days of the week, today actually Wednesday
and Satturday. Since many centuries renown foreing visitors have
explored them and wrote their elegy about, included Nort-American
geoglogues.”</i><br />
  [From a tourist leaflet.]</p>

  <p>Such subtle filigranity and nobless of construccion<br />
  Here fraternise in harmony, that respiracion stops.<br />
While all admit their impotence (though autors most formidable)<br />
  To sing in words the excellence of Nature’s underprops,<br />
Yet stalactite and stalagmite together with dumb langage<br />
  Make hymnes to God which celebrate the stregnth of water drops.</p>

  <p>¿You, also, are you capable to make precise in idiom<br />
  Consideracions magic of ilusions very wide?<br />
Already in the Vestibule of these Grand Caves of Arta<br />
  The spirit of the human verb is darked and stupefied;<br />
So humildy you trespass trough the forest of the colums<br />
  And listen to the grandess explicated by the guide.</p>

  <p>From darkness into darkness, but at measure now descending,<br />
  You remark with what esxactitude he designates each bent;<br />
«The Saloon of Thousand Banners», or «The Tumba of Napoleon»,<br />
  «The Grotto of the Rosary», «The Club», «The Camping Tent»,<br />
And at «Cavern of the Organs» there are knocking streange formacions<br />
  Which give a nois particular pervoking wonderment.</p>

  <p>Too far do not adventure, sir! For, further as you wander,<br />
  The every of stalactites will make you stop and stay.<br />
Grand peril amenaces now, your nostrills aprehending<br />
  An odour least delicious of lamentable decay.<br />
<i><span style="font-variant-caps: small-caps;">It is poor touristers, in the depth of obscure cristal,</span></i><br />
  <i><span style="font-variant-caps: small-caps;">Which deceased of their emocion on a past excursion day.</span></i></p>
</blockquote>]]></content><author><name></name></author><category term="blog-roundup" /><category term="litclub" /><category term="poetry" /><category term="word-ways" /><summary type="html"><![CDATA[I recently read, and enjoyed, David Owen’s “The Objectively Objectionable Grammatical Pet Peeve” (January 2023). The objectionable practice in question is what Fowler calls the “sentry participle,” a participle or appositive stuck on the front of a sentence that could just as well have done without it. For example:]]></summary></entry><entry><title type="html">`has_unique_object_representations` versus `__is_trivially_equality_comparable`</title><link href="https://quuxplusone.github.io/blog/2026/02/14/has-unique-object-representations/" rel="alternate" type="text/html" title="`has_unique_object_representations` versus `__is_trivially_equality_comparable`" /><published>2026-02-14T00:01:00+00:00</published><updated>2026-02-14T00:01:00+00:00</updated><id>https://quuxplusone.github.io/blog/2026/02/14/has-unique-object-representations</id><content type="html" xml:base="https://quuxplusone.github.io/blog/2026/02/14/has-unique-object-representations/"><![CDATA[<p>Yesterday on the <a href="https://cppalliance.org/slack/">cpplang Slack</a>,
someone asked the purpose of <code class="language-plaintext highlighter-rouge">std::has_unique_object_representations_v&lt;T&gt;</code>,
and someone else pointed to an example given in
Steve Dewhurst’s <a href="https://www.youtube.com/watch?v=SShSV_iV1Ko">“Back to Basics: Class Layout”</a> (2020).
Sadly, that example is incorrect. Let’s take a look.</p>

<p>Steve presents this code snippet (<a href="https://www.youtube.com/watch?v=SShSV_iV1Ko&amp;t=1738s">@28m58s</a>):</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>class NarrowLand {
  unsigned long long x_;
  unsigned long long y_;
  unsigned long long z_;
  friend bool operator==(const NarrowLand&amp; a, const NarrowLand&amp; b) {
    return memcmp(&amp;a, &amp;b, sizeof(a)) == 0;
  }
};
</code></pre></div></div>

<p>The programmer knows this implementation of <code class="language-plaintext highlighter-rouge">operator==</code> is “safe” because <code class="language-plaintext highlighter-rouge">unsigned long long</code> itself
is trivially equality-comparable and because <code class="language-plaintext highlighter-rouge">NarrowLand</code> contains no padding bits in between
its <code class="language-plaintext highlighter-rouge">unsigned long long</code> fields, nor at the end of the class. (It isn’t allowed to contain
padding at the <em>start</em> of the class, because it is a <a href="https://eel.is/c++draft/class.prop#def:class,standard-layout">standard-layout class type</a>.)
Therefore comparing two <code class="language-plaintext highlighter-rouge">NarrowLand</code> objects is tantamount to comparing their bytes —
their <em>object representations</em> — and can be done with a simple memcmp.</p>

<p>But you should never write that memcmp yourself! Since C++20, you should simply <em>default</em>
your comparison operator, like this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>class BetterLand {
  unsigned long long x_;
  unsigned long long y_;
  unsigned long long z_;
  friend bool operator==(const BetterLand&amp;, const BetterLand&amp;) = default;
};
</code></pre></div></div>

<p>On Clang, <code class="language-plaintext highlighter-rouge">BetterLand</code>’s <code class="language-plaintext highlighter-rouge">operator==</code> produces exactly the same codegen as <code class="language-plaintext highlighter-rouge">NarrowLand</code>’s;
on GCC, <code class="language-plaintext highlighter-rouge">BetterLand</code>’s codegen is better. (<a href="https://godbolt.org/z/PEbE3KMd7">Godbolt.</a>)</p>

<p>But the real reason to prefer <code class="language-plaintext highlighter-rouge">BetterLand</code> — to let the compiler default the comparison for you,
instead of user-defining it — is that defaulted operations are more legible to the compiler.
In the Godbolt above, notice that Clang (since Clang 17) understands that
<code class="language-plaintext highlighter-rouge">__is_trivially_equality_comparable(BetterLand)</code>, whereas Clang refuses to “crack open the curly braces”
of <code class="language-plaintext highlighter-rouge">NarrowLand</code>’s <code class="language-plaintext highlighter-rouge">operator==</code> to prove anything useful about that one. This type-trait feeds into
(among other places) libc++’s implementation of <code class="language-plaintext highlighter-rouge">std::equal</code>, so that we can write (<a href="https://godbolt.org/z/cGd358Yr4">Godbolt</a>)</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bool test(std::vector&lt;BetterLand&gt;&amp; a, std::vector&lt;BetterLand&gt;&amp; b) {
  return a == b;
}
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">vector</code>’s <code class="language-plaintext highlighter-rouge">operator==</code> dispatches to <code class="language-plaintext highlighter-rouge">std::equal</code>, which sees that we’re comparing two contiguous
arrays of trivially equality-comparable types and so it generates a single <code class="language-plaintext highlighter-rouge">bcmp</code> for the entire comparison.
Contrast to the opaque, hand-coded <code class="language-plaintext highlighter-rouge">NarrowLand</code>, which for this same snippet generates a loop over many
short (12-byte) <code class="language-plaintext highlighter-rouge">memcmp</code>s. <code class="language-plaintext highlighter-rouge">NarrowLand</code> is <em>vastly</em> worse than <code class="language-plaintext highlighter-rouge">BetterLand</code> in this example!</p>

<h2 id="okay-but-has_unique_object_representations">Okay, but <code class="language-plaintext highlighter-rouge">has_unique_object_representations</code>…?</h2>

<p>Right. In Steve’s 2020 talk, he shows two ways that the hand-optimized <code class="language-plaintext highlighter-rouge">NarrowLand</code> could (under maintenance)
become not only inefficient but actually wrong. First, you could accidentally introduce padding bytes:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>class BadLand1 {
  unsigned char x_; // oops, 7 bytes of padding introduced here
  unsigned long long y_;
  unsigned long long z_;
  friend bool operator==(const NarrowLand&amp; a, const NarrowLand&amp; b) {
    // oops, the outcome now depends on indeterminate values
    return memcmp(&amp;a, &amp;b, sizeof(a)) == 0;
  }
};
</code></pre></div></div>

<p>Second, you could accidentally change <code class="language-plaintext highlighter-rouge">unsigned long long</code> to a type that isn’t itself trivially equality-comparable:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>class BadLand2 {
  double x_;
  double y_;
  double z_;
  friend bool operator==(const NarrowLand&amp; a, const NarrowLand&amp; b) {
    // oops, +0.0 and -0.0 now compare unequal instead of equal
    return memcmp(&amp;a, &amp;b, sizeof(a)) == 0;
  }
};
</code></pre></div></div>

<p>Given these correctness pitfalls, Steve’s first recommendation is admirably in line with
<a href="https://en.wikipedia.org/wiki/Michael_A._Jackson_(computer_scientist)">Michael A. Jackson’s</a> famous
“Rules of Program Optimization.” In <a href="https://archive.org/details/principlesofprog00jack/page/n10"><em>Principles of Program Design</em></a> (1975),
Jackson writes:</p>

<blockquote>
  <p>[In] the examples used throughout the book […] optimization is avoided. We follow two rules in the matter of optimization:</p>

  <p>Rule 1. Don’t do it.<br />
Rule 2 (for experts only): Don’t do it yet. […]</p>

  <p>Two points should always be remembered: first, optimization makes a system less reliable and harder to maintain,
and therefore more expensive to build and operate; second, because optimization obscures structure it is difficult
to improve the efficiency of a system which is already partly optimized.</p>
</blockquote>

<p>Arguably we’ve already seen an example of Jackson’s second point: libc++ was unable to give us the best codegen for <code class="language-plaintext highlighter-rouge">vector&lt;NarrowLand&gt;</code>
because the structure of <code class="language-plaintext highlighter-rouge">NarrowLand</code>’s own <code class="language-plaintext highlighter-rouge">operator==</code> was “obscured” by its “partly optimized” implementation.</p>

<p>Steve then suggests (<a href="https://www.youtube.com/watch?v=SShSV_iV1Ko&amp;t=1935s">@32m15s</a>) that, if for some reason you
<em>must</em> do this partial optimization, you could improve its reliability under maintenance by adding:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>static_assert(std::has_unique_object_representations_v&lt;NarrowLand&gt;);
</code></pre></div></div>

<p>Indeed this <code class="language-plaintext highlighter-rouge">static_assert</code> would fail for either <code class="language-plaintext highlighter-rouge">BadLand1</code> or <code class="language-plaintext highlighter-rouge">BadLand2</code>. But, I cannot stress enough,
<em>this is not a correct use of <code class="language-plaintext highlighter-rouge">has_unique_object_representations</code>!</em>  In fact I don’t think there is any “correct”
use of <code class="language-plaintext highlighter-rouge">has_unique_object_representations</code>; I think it is a trait without a meaningful purpose.</p>

<p>“But,” you ask, “if it rejects <code class="language-plaintext highlighter-rouge">BadLand1</code> and <code class="language-plaintext highlighter-rouge">BadLand2</code>, and accepts <code class="language-plaintext highlighter-rouge">NarrowLand</code>, then isn’t
it basically the same as the trait you and Clang call <code class="language-plaintext highlighter-rouge">__is_trivially_equality_comparable</code>? Isn’t it kind of like
the ‘portable spelling’ of <code class="language-plaintext highlighter-rouge">__is_trivially_equality_comparable</code>?” Sadly, no. <a href="https://eel.is/c++draft/meta.unary#prop-10">[meta.unary.prop]/10</a>:</p>

<blockquote>
  <p>The predicate condition for a template specialization <code class="language-plaintext highlighter-rouge">has_unique_object_representations&lt;T&gt;</code>
shall be satisfied if and only if</p>
  <ul>
    <li><code class="language-plaintext highlighter-rouge">T</code> is trivially copyable, and</li>
    <li>any two objects of type <code class="language-plaintext highlighter-rouge">T</code> with the same value have the same object representation, where
      <ul>
        <li>two objects of array or non-union class type are considered to have the same value
if their respective sequences of direct subobjects have the same values, and</li>
        <li>two objects of union type are considered to have the same value if they have the
same active member and the corresponding members have the same value.</li>
      </ul>
    </li>
  </ul>

  <p>The set of scalar types for which this condition holds is implementation-defined.</p>
</blockquote>

<p>Here are five examples where the two traits give different answers.</p>

<h2 id="1-libcs-optionalchar-false-positive">1. libc++’s <code class="language-plaintext highlighter-rouge">optional&lt;char&gt;</code> (false positive)</h2>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>using T = std::optional&lt;char&gt;;
static_assert(std::has_unique_object_representations_v&lt;T&gt;);
static_assert(!__is_trivially_equality_comparable(T));
</code></pre></div></div>

<p>(<a href="https://godbolt.org/z/vcxG47cKK">Godbolt.</a>) This is the simplest example of how <code class="language-plaintext highlighter-rouge">has_unique_object_representations</code>
doesn’t care about comparison semantics. All it looks at is trivial copyability (libc++’s <code class="language-plaintext highlighter-rouge">optional&lt;char&gt;</code> is trivially
copyable) and the object representation, which for <code class="language-plaintext highlighter-rouge">optional</code> is simply:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>union {
  char dummy_;
  char value_;
};
bool has_value_;
</code></pre></div></div>

<p>(You might ask why <code class="language-plaintext highlighter-rouge">has_unique_object_representations_v&lt;bool&gt;</code> is considered <code class="language-plaintext highlighter-rouge">true</code> instead of <code class="language-plaintext highlighter-rouge">false</code>,
given that <code class="language-plaintext highlighter-rouge">bool</code> has seven padding bits. I’m hazy on the details myself, but I believe it has to do
with what Clang will soon call <a href="https://github.com/llvm/llvm-project/pull/160790"><code class="language-plaintext highlighter-rouge">-fstrict-bool</code></a>: by default
Clang assumes that all seven of those padding bits are zero, permitting it to codegen the optimal trivial
comparison; only with <code class="language-plaintext highlighter-rouge">-fstrict-bool=truncate</code> or <code class="language-plaintext highlighter-rouge">-fstrict-bool=nonzero</code> will Clang ever generate non-trivial
code for bool equality-comparison.)</p>

<p>If you assume <code class="language-plaintext highlighter-rouge">optional&lt;char&gt;</code>’s comparison can be lowered to memcmp, you’ll sometimes think
that equal (disengaged) optionals are unequal.</p>

<h2 id="2-optionalint-false-positive">2. <code class="language-plaintext highlighter-rouge">optional&lt;int&amp;&gt;</code> (false positive)</h2>

<p>C++26 <code class="language-plaintext highlighter-rouge">optional&lt;int&amp;&gt;</code> has the same issue (<a href="https://godbolt.org/z/8e3EeKod1">Godbolt</a>), and here the behavior
doesn’t depend on your STL vendor: <code class="language-plaintext highlighter-rouge">optional&lt;int&amp;&gt;</code> is guaranteed to be trivially copyable on all implementations,
and vice versa its <code class="language-plaintext highlighter-rouge">operator==</code> is guaranteed to compare the value of the <em>referred-to</em> <code class="language-plaintext highlighter-rouge">int</code> object, rather
than the value of the <code class="language-plaintext highlighter-rouge">optional</code>’s pointer data member.</p>

<p>If you assume <code class="language-plaintext highlighter-rouge">optional&lt;int&amp;&gt;</code>’s comparison can be lowered to memcmp, you’ll sometimes think
that equal optionals (referring to distinct <code class="language-plaintext highlighter-rouge">int</code> objects with equal values) are unequal.</p>

<h2 id="3-pmrpolymorphic_allocatorint-false-positive">3. <code class="language-plaintext highlighter-rouge">pmr::polymorphic_allocator&lt;int&gt;</code> (false positive)</h2>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>using T = std::pmr::polymorphic_allocator&lt;int&gt;;
static_assert(std::has_unique_object_representations_v&lt;T&gt;);
static_assert(!__is_trivially_equality_comparable(T));
</code></pre></div></div>

<p>(<a href="https://godbolt.org/z/cndG1xczn">Godbolt.</a>) <code class="language-plaintext highlighter-rouge">polymorphic_allocator</code> is trivially copyable,
and it has only a single <code class="language-plaintext highlighter-rouge">memory_resource*</code> data member. But its <code class="language-plaintext highlighter-rouge">operator==</code> doesn’t compare
the value of that pointer; it does a “deep comparison” via virtual dispatch to <code class="language-plaintext highlighter-rouge">memory_resource::do_equal</code>.</p>

<p>If you assume <code class="language-plaintext highlighter-rouge">polymorphic_allocator</code>’s comparison can be lowered to memcmp, you’ll sometimes think
that equal (compatible) allocators are unequal.</p>

<h2 id="4-spanint-false-positive">4. <code class="language-plaintext highlighter-rouge">span&lt;int&gt;</code> (false positive)</h2>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>using T = std::span&lt;int&gt;;
static_assert(std::has_unique_object_representations_v&lt;T&gt;);
static_assert(!__is_trivially_equality_comparable(T));
</code></pre></div></div>

<p>(<a href="https://godbolt.org/z/o9vjM6zhh">Godbolt.</a>) This is a silly trivial example, included for completeness.
<code class="language-plaintext highlighter-rouge">std::span</code> simply doesn’t implement equality-comparison at all — <code class="language-plaintext highlighter-rouge">equality_comparable&lt;span&lt;int&gt;&gt;</code> is
<code class="language-plaintext highlighter-rouge">false</code> — so obviously it is not trivially equality-comparable either.
But <code class="language-plaintext highlighter-rouge">has_unique_object_representations</code> doesn’t care about equality comparison! <code class="language-plaintext highlighter-rouge">span</code> is trivially copyable,
and has only scalar data members with no padding, so <code class="language-plaintext highlighter-rouge">has_unique_object_representations</code> yields <code class="language-plaintext highlighter-rouge">true</code>.</p>

<h2 id="5-libstdcs-exception_ptr-false-negative">5. libstdc++’s <code class="language-plaintext highlighter-rouge">exception_ptr</code> (false negative)</h2>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>using T = std::exception_ptr;
static_assert(!std::has_unique_object_representations_v&lt;T&gt;);
static_assert(__is_trivially_equality_comparable(T));
</code></pre></div></div>

<p>(<a href="https://godbolt.org/z/EKb7xx6os">Godbolt.</a>)
We saw above that <a href="https://eel.is/c++draft/meta.unary#prop-10">[meta.unary.prop]/10</a> tells <code class="language-plaintext highlighter-rouge">has_unique_object_representations</code>
to reject any type that’s not trivially copyable. This rules out many resource-owning types which
are nevertheless trivially equality-comparable. The simplest example is <code class="language-plaintext highlighter-rouge">exception_ptr</code>, which holds
merely a pointer to a heap-allocated exception object; two <code class="language-plaintext highlighter-rouge">exception_ptr</code>s are equal if and only if
they hold the same pointer. Yet, because it’s an <em>owning</em> pointer, <code class="language-plaintext highlighter-rouge">exception_ptr</code> is not trivially copyable:
therefore <code class="language-plaintext highlighter-rouge">has_unique_object_representations</code> rejects it.</p>

<h3 id="rabbit-hole-unique_ptrint">Rabbit hole: <code class="language-plaintext highlighter-rouge">unique_ptr&lt;int&gt;</code></h3>

<p>Another example, you might think, would be <code class="language-plaintext highlighter-rouge">unique_ptr&lt;int&gt;</code>: it’s isomorphic to <code class="language-plaintext highlighter-rouge">exception_ptr</code>
in all important respects (resource-owning, otherwise just a pointer).
But in fact <code class="language-plaintext highlighter-rouge">unique_ptr</code> is a complicated case, because <code class="language-plaintext highlighter-rouge">unique_ptr</code> doesn’t hold <em>just</em> a pointer;
it also holds a deleter. <code class="language-plaintext highlighter-rouge">unique_ptr&lt;int&gt;</code>’s layout is really more like:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[[no_unique_address]] default_delete&lt;int&gt; deleter_;
int *ptr_;
</code></pre></div></div>

<p>And <code class="language-plaintext highlighter-rouge">default_delete&lt;int&gt;</code> has no <code class="language-plaintext highlighter-rouge">operator==</code>. Therefore <code class="language-plaintext highlighter-rouge">unique_ptr</code> cannot default its own <code class="language-plaintext highlighter-rouge">operator==</code>;
therefore it must have a <em>user-defined</em> <code class="language-plaintext highlighter-rouge">operator==</code>; therefore the compiler refuses to “crack open the
curly braces” and reports that <code class="language-plaintext highlighter-rouge">unique_ptr</code> is not (known to be) trivially equality-comparable.</p>

<p>Could Clang make <code class="language-plaintext highlighter-rouge">unique_ptr</code> trivially equality-comparable? Theoretically, yes, in either of two ways.
The easy, but politically fraught, way would be for Clang to add a “warranting” attribute similar
to P1144’s <code class="language-plaintext highlighter-rouge">[[trivially_relocatable]]</code>, and for libc++ to use it:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>template&lt;
  class T, class Deleter = default_delete&lt;T&gt;,
  bool TEC = is_empty_v&lt;Deleter&gt; &amp;&amp; __is_trivially_equality_comparable(Deleter::pointer)
&gt;
class [[fantasy::trivially_equality_comparable(TEC)]] unique_ptr {
  [[no_unique_address]] Deleter d_;
  Deleter::pointer p_;
  friend bool operator==(const unique_ptr&amp; a, const unique_ptr&amp; b) {
    return a.p_ == b.p_;
  }
};
</code></pre></div></div>

<p>The more general-purpose, but harder, way would be for Clang’s implementation of
<code class="language-plaintext highlighter-rouge">__is_trivially_equality_comparable</code> to understand the following convoluted idiom,
and for libc++ to use it:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>struct EmptyComparable {
  bool operator==(const EmptyComparable&amp;) const = default;
};

template&lt;class T, class Deleter = default_delete&lt;T&gt;&gt;
class unique_ptr {
  struct Helper : EmptyComparable {
    [[no_unique_address]] Deleter deleter_;
  };
  [[no_unique_address]] Helper d_;
  Deleter::pointer p_;
  friend bool operator==(const unique_ptr&amp; a, const unique_ptr&amp; b) = default;
};
</code></pre></div></div>

<p>Here we have produced correct behavior for <code class="language-plaintext highlighter-rouge">operator==</code> entirely from defaulted
definitions. So, in theory, the compiler has enough information to understand our
<code class="language-plaintext highlighter-rouge">operator==</code>’s behavior; someone just has to teach the compiler to disentangle that information
and use it effectively. <a href="https://godbolt.org/z/j3qxEjbEs">Clang doesn’t do this today.</a></p>

<p>This latter approach, however, is vastly complicated by <code class="language-plaintext highlighter-rouge">unique_ptr</code>’s
<a href="https://en.cppreference.com/w/cpp/memory/unique_ptr/operator_cmp.html">many overloads of <code class="language-plaintext highlighter-rouge">==</code></a>.</p>

<h2 id="conclusion-and-postscript">Conclusion and postscript</h2>

<p>In short: <code class="language-plaintext highlighter-rouge">has_unique_object_representations</code> is not a suitable substitute for
<code class="language-plaintext highlighter-rouge">__is_trivially_equality_comparable</code>. You <em>must not</em> use <code class="language-plaintext highlighter-rouge">has_unique_object_representations</code>
to mean “trivially equality-comparable” in generic code. It often reports <code class="language-plaintext highlighter-rouge">true</code> for
types that aren’t trivially equality-comparable; it sometimes reports <code class="language-plaintext highlighter-rouge">false</code> for
types that are trivially equality-comparable.</p>

<p>I claim that <code class="language-plaintext highlighter-rouge">has_unique_object_representations</code> has <em>absolutely no valid use-case.</em></p>

<p>P.S. — “Could <code class="language-plaintext highlighter-rouge">has_unique_object_representations</code> have something to do with hashing?” Nope!
Hashing is tightly coupled to equality-comparison: if <code class="language-plaintext highlighter-rouge">a == b</code> then we must have <code class="language-plaintext highlighter-rouge">h(a) == h(b)</code>.
Nobody can say whether a particular (perhaps trivial) hash function <code class="language-plaintext highlighter-rouge">h</code> is appropriate for
a <code class="language-plaintext highlighter-rouge">T</code> unless they understand <code class="language-plaintext highlighter-rouge">T</code>’s <code class="language-plaintext highlighter-rouge">operator==</code>, and as we’ve seen, <code class="language-plaintext highlighter-rouge">has_unique_object_representations</code>
doesn’t know or care about <code class="language-plaintext highlighter-rouge">==</code>. In fact, no part of the compiler knows anything about <code class="language-plaintext highlighter-rouge">std::hash</code>
at all; unlike <code class="language-plaintext highlighter-rouge">==</code>, <code class="language-plaintext highlighter-rouge">std::hash</code> is a pure library facility. Luckily, logic dictates
that any trivially equality-comparable <code class="language-plaintext highlighter-rouge">T</code> can be hashed by hashing its object representation:
this might produce a suboptimal hash function, but it will never distribute equal <code class="language-plaintext highlighter-rouge">T</code>s into
unequal hash buckets. So there is no need for an <code class="language-plaintext highlighter-rouge">__is_trivially_hashable</code> trait; it winds up
identical to <code class="language-plaintext highlighter-rouge">__is_trivially_equality_comparable</code>, just as
<a href="/blog/2018/06/29/trivially-swappable/"><code class="language-plaintext highlighter-rouge">__is_trivially_swappable</code> winds up identical to P1144 <code class="language-plaintext highlighter-rouge">__is_trivially_relocatable</code></a>.</p>

<p>However, thanks to Howard Hinnant for pointing out that the paper trail behind
<code class="language-plaintext highlighter-rouge">has_unique_object_representations</code> did indeed cite “hashing” as its original motivation: see
<a href="https://github.com/HowardHinnant/hash_append/blob/17a19a2/hash_append.h#L115-L120">Howard’s trait <code class="language-plaintext highlighter-rouge">is_uniquely_represented</code></a>,
which became <a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0029r0.html">P0029</a>,
<a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0258r0.html">P0258R0 <code class="language-plaintext highlighter-rouge">is_contiguous_layout</code></a>,
and finally <a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0258r2.html">P0258R2 <code class="language-plaintext highlighter-rouge">has_unique_object_representations</code></a>.
It’s just too bad that <code class="language-plaintext highlighter-rouge">has_unique_object_representations</code> completely failed to achieve its goal
and is now useless.</p>]]></content><author><name></name></author><category term="cpplang-slack" /><category term="operator-spaceship" /><category term="triviality" /><category term="type-traits" /><category term="value-semantics" /><summary type="html"><![CDATA[Yesterday on the cpplang Slack, someone asked the purpose of std::has_unique_object_representations_v&lt;T&gt;, and someone else pointed to an example given in Steve Dewhurst’s “Back to Basics: Class Layout” (2020). Sadly, that example is incorrect. Let’s take a look.]]></summary></entry><entry><title type="html">KenKen with Roman numerals</title><link href="https://quuxplusone.github.io/blog/2026/02/10/roman-kenken/" rel="alternate" type="text/html" title="KenKen with Roman numerals" /><published>2026-02-10T00:01:00+00:00</published><updated>2026-02-10T00:01:00+00:00</updated><id>https://quuxplusone.github.io/blog/2026/02/10/roman-kenken</id><content type="html" xml:base="https://quuxplusone.github.io/blog/2026/02/10/roman-kenken/"><![CDATA[<p>The other day my wife suggested the idea of <a href="https://en.wikipedia.org/wiki/KenKen">KenKen</a> “with letters.”
There’s probably an idea in there related to anagrams, somehow; but the first idea that popped into my
head was to use Roman numerals. Here’s a “<em>really</em> Latin square” for you to solve:</p>

<p>Fill in each cell in this 4×4 grid with one of the Roman numerals C, L, X, V, such that each numeral
appears exactly once in each row and in each column, and each of the three dark-bordered “cages” contains
numerals summing exactly to the given value.</p>

<p><img src="/blog/images/2026-02-10-roman-kenken.png" alt="" /></p>

<p>I don’t do enough KenKen to know if this puzzle is unusually easy, or difficult, or whatever.</p>

<p>KenKen newbies should note that the label “CCX+” means “this cage sums to <em>exactly</em> 210”; the plus sign
is there merely to indicate the math operation being used. Ordinary KenKen puzzles feature cages with
not just addition but also subtraction, multiplication, and division.
KenKen puzzles featuring only multiplication are known as
<a href="https://en.wikipedia.org/wiki/Inshi_no_heya"><em>inshi no heya</em></a>; I don’t know if there’s a name
for puzzles like this one featuring only addition.</p>

<p>This puzzle also has two unlabeled cages; that’s not typical of the genre, and probably shows how little
expertise I have in KenKen construction. But labeling them would have made the puzzle <em>definitely</em> too easy.</p>]]></content><author><name></name></author><category term="math" /><category term="puzzles" /><summary type="html"><![CDATA[The other day my wife suggested the idea of KenKen “with letters.” There’s probably an idea in there related to anagrams, somehow; but the first idea that popped into my head was to use Roman numerals. Here’s a “really Latin square” for you to solve:]]></summary></entry><entry><title type="html">Mysteries of the map, partly answered</title><link href="https://quuxplusone.github.io/blog/2026/01/19/long0751-map-mysteries-2/" rel="alternate" type="text/html" title="Mysteries of the map, partly answered" /><published>2026-01-19T00:01:00+00:00</published><updated>2026-01-19T00:01:00+00:00</updated><id>https://quuxplusone.github.io/blog/2026/01/19/long0751-map-mysteries-2</id><content type="html" xml:base="https://quuxplusone.github.io/blog/2026/01/19/long0751-map-mysteries-2/"><![CDATA[<p>Previously: <a href="/blog/2025/12/29/long0751/">“LONG0751 has been found”</a> (no spoilers),
<a href="/blog/2025/12/30/long0751-map-mysteries/">“LONG0751: Mysteries of Donovan’s map”</a> (minor spoilers).
This post contains <b>major</b> spoilers for LONG0751!</p>

<p>This post is an expansion of something
<a href="https://forums.delphiforums.com/xyzzy/messages/?msg=527.11">I wrote</a>
in the Delphi Colossal Cave forum a few days ago.</p>

<p>In my previous post I observed that there are some discrepancies and omissions in
Dennis Donovan’s November 1980
<a href="https://www.club.cc.cmu.edu/~ajo/in-search-of-LONG0751/compuserve-map/New-Adventure-Map.pdf">map</a>,
compared to the game we recovered (<code class="language-plaintext highlighter-rouge">ADVENTURE &lt; 6.1/ 3&gt;, 14-Jan-82</code>).
For example, Donovan’s map is missing what we might call the “helicopter arc,” comprising
the Conservatory (letter of transit), helipad, silver mine (pumps, ingots),
Secret Garden (parachute, golden apple), and kitchen (dumbwaiter). I theorize that
this is no mistake: Donovan’s map is based on a “missing link” version of <em>Adventure 6</em>
that didn’t yet include those elements. But this “missing link” theory created
a few puzzles of its own:</p>

<h2 id="the-mystery-of-the-well-bottom">The mystery of the well bottom</h2>

<p>In the recovered game, there are two ways into the castle. You can take the helicopter;
or, you can go past the leprechaun and boulder to the bottom of the dry well, where you
must PLAY FLUTE (thrice) to charm the rope and climb up it before it falls. (The well is
the only location where PLAY FLUTE charms the rope, as far as I know. For example it can’t
help you ascend in the Rainbow Room.)
But the flute is found in the Conservatory, which doesn’t exist on Donovan’s map.
So how did this puzzle work in the “missing-link” game?</p>

<p>Today I’m pretty confident in the following theory: In the recovered game, you find an “iron handle”
at the bottom of the well, which is required in order to open the drawbridge, which is required
in order to defeat Ralph the Centipede. Donovan’s map depicts a long straight object at the
bottom of the well; naturally I assumed this was the winch handle. However, it’s not! In the missing-link
game, I theorize, <em>this is where you find the flute.</em> Look closely at the depicted object:
what I originally took for cross-hatching on an iron bar is now clearly suggestive of
finger holes in a flute!</p>

<p>That’s just good puzzle design: the game presents
an impossible vertical ascent plus a flute, and all you have to do is make the mental connection to
the Hindu rope trick and go find some rope. It also lampshades why PLAY FLUTE works only in this
specific location. By comparison, the 1982 game’s puzzle is unfairly difficult and unsatisfyingly
non-sequitur.</p>

<p>So, my theory goes, Long originally had the flute at the bottom of the well, and a fully
operational drawbridge (no handle needed). When he added the helicopter, he needed a place
to put the letter of transit. Naturally <a href="https://www.dailyscript.com/scripts/casablanca.pdf">it goes in Sam’s piano</a>,
and the piano goes in a newly added Conservatory of Music. Naturally, then, the flute also moves to the Conservatory.
Long now kills two birds with one stone: To give the player some reason to move the boulder and visit the
well-bottom, and perhaps also to explain the long thin object depicted on Donovan’s map, he dismantles the
drawbridge and places the handle down there.</p>

<h2 id="stratigraphy-via-room-numbers">Stratigraphy via room numbers</h2>

<p>The recovered game’s data file (<a href="https://github.com/Quuxplusone/Advent/blob/master/LONG0751/decompiled-advdat.txt">on my GitHub here</a>)
lets us see the mapping from rooms and objects to numbers. The room numbers of LONG0751
are almost precisely a superset of the room numbers of ANON0501 and MCDO0551 (that is, except for those games’
additions), and those games’ rooms are precise supersets of WOOD0350.</p>

<p>LONG0751’s room numbers support my missing-link theory: after LONG0501 ends at room 238,
we see the approach to the castle (240–247, with the parking-lot puzzle), then the
bramblebush puzzle (249) and Sham Rock (250, without the briar pipe at first), and then
the castle and well as depicted by Donovan (251–272) intermingled with the Elephants’
Burial Ground and boulder puzzle (259, 261) and the Centipede’s Lair (268).
Donovan’s map corresponds to this version of the game, plus or minus
two nondescript castle hallways (273–274).</p>

<p>Then we get the Conservatory (275), helipad (276–283),
mine entrance (284–287), and Secret Garden (288–292). At this point there was still
no engineering room in the mines, no flooding, no statue or wooden box.
Then we get some rat-related ravine stuff (293–297) that makes me think the rat’s lair
was originally somewhere in this area instead of beyond the mosquitos (still no briar
pipe at this point). Then the pumps and dumbwaiter setpieces (298–302). Finally
the rat’s new lair (302–303).</p>

<h2 id="stratigraphy-via-object-numbers">Stratigraphy via object numbers</h2>

<p>The object numbers, on the other hand, seem to wiggle around much more.
Partly this seems to be due to Long’s filling in old gaps with new objects.
Gaps have existed since the beginning: WOOD0350 has no objects at 41–49,
because Woods moved CROW0005’s treasures to start contiguously at 50–64;
Woods also moved the steps and bird (née 6–7) to 7–8 in order to insert his
second black rod right after the first one.
Likewise it seems that Long kept shuffling items, creating gaps and backfilling
them with new items. Some of these shuffles I can explain pretty well, and
some I can’t:</p>

<ul>
  <li>
    <p>WOOD0350’s liquids-in-vessels (water and oil, in the bottle) are at 21–22.
  LONG0501 adds the wine and the cask, and moves them all to 81–86.
  LONG0501 fills the gap at 21–22 with the troll bridge (née 32) and clay bridge.
  LONG0751 fills 32 with the briar pipe.</p>
  </li>
  <li>
    <p>WOOD0350’s rusty door is object 9. LONG0501 adds the tiny door and phone-booth
  door(s), moving all four to 41–44 and filling the gap at 9 with the pole.
  LONG0751 adds the castle door, vault door, and garden door, keeping the doors
  contiguous at 41–47 even though this means moving the flowers from 46 to 173
  and the cloak from 47 to 76.</p>
  </li>
  <li>
    <p>WOOD0350’s golden chain is object 64. LONG0751 adds the iron chain, moving
  both to 202–203. LONG0751 fills 64 with the swamp’s “buzzing sound.”</p>
  </li>
  <li>
    <p>WOOD0350’s set of keys is object 1. LONG0501 adds the tiny brass key (90)
  and inexplicably moves the keys to 102. LONG0751 fills 1 with the large boulder.</p>
  </li>
  <li>
    <p>WOOD0350’s little bird is object 8. LONG0501 inexplicably moves it to 101.
  LONG0751 adds the black bird, keeping them contiguous at 101–102
  even though this further displaces the set of keys from 102 to 174.</p>
  </li>
  <li>
    <p>LONG0751 inexplicably moves objects 35–40 (bear through moss) to 212–217,
  leaving an unfilled gap.</p>
  </li>
</ul>

<p>Rather than try to paraphrase <em>all</em> the changes, I’ve collated the object numbers
from all four versions (CROW0005, WOOD0350, LONG0501, LONG0751) in a CSV file
<a href="/blog/code/2026-01-19-object-stratigraphy.csv">here</a>. I imagine someone could
make a nice colorful flow diagram out of that data, somehow.
(If you do, <a href="mailto:arthur.j.odwyer@gmail.com">email me!</a>)</p>

<h2 id="the-mystery-of-the-candle">The mystery of the candle</h2>

<p>Donovan’s map depicts a candle at the Altar, despite the fact that LONG0751 uses
the candle only in the dumbwaiter puzzle, which doesn’t yet exist in Donovan’s
“missing-link” game. What was the purpose of the candle in the missing-link game?
I have no good theory.</p>

<p>One wild-ass conjecture is that the missing-link game had the candle at either 74 or 76,
adjacent to the brambles (75), and the puzzle was simply to burn the brambles with
the candle. (Donovan depicts the candle as already lighted; and BURN BUSH still
“works” in LONG0751, although now it destroys the rose, which in this hypothetical
scenario didn’t exist yet.) At some point Long added the matches (127–133)
and moved the candle thematically to 134, before adding the rose (135–136)
and changing the intended solution to the bramblebush puzzle. But why would Long
add the matches before the dumbwaiter existed, and long before the briar pipe?
Unless you used the briar pipe to smoke out the <em>centipede</em> rather than the mosquitoes…
But this is conspiracy-theory reasoning: “How could X be true? Only if Y; and Y couldn’t
be true unless Z,” and soon you’ve reasoned your way far off the true path.</p>

<blockquote>
  <p>I recently read Joe Klaas’
<a href="https://archive.org/details/ameliaearhartliv00klaa"><em>Amelia Earhart Lives!</em></a> (1970),
which exemplifies this kind of reasoning: Earhart couldn’t have vanished unless
she was shot down by the Japanese; but Earhart couldn’t have been shot down
by the Japanese unless she had been spy-photographing their secret base at Truk;
but her plane couldn’t have reached Truk unless it secretly had super-powered
engines; and so on.</p>
</blockquote>

<p>The sensible but boring theory is that the candle was just set-dressing,
and didn’t <em>do</em> anything until later.</p>

<h2 id="vestigial-treasures">Vestigial treasures</h2>

<p>The recovered game’s data file (<a href="https://github.com/Quuxplusone/Advent/blob/master/LONG0751/decompiled-advdat.txt">on my GitHub here</a>)
contains definitions for five objects apparently inaccessible within the game itself:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>142     beautiful painting
0000    A beautiful painting is hung on the wall.
1000    There is a beautiful painting here!
150     tarnhelm
0000    There is a round iron helmet here.
162     rare stamp
0000    There is a rare postage stamp here!
163     golden necklace
0000    There is a golden necklace here!
164     valuable furs
0000    Nearby, some valuable furs have been strewn about.
</code></pre></div></div>

<p>Sadly none of these seem to be the item depicted in the Great Hall on Donovan’s map.</p>

<p>The painting is placed among the golden apple (141), tapestry (143), fleece (144),
and perfume (145); perhaps these treasures were added in one go.</p>

<p>The tarnhelm (<a href="https://en.wikipedia.org/wiki/Tarnhelm">a magic helmet that grants invisibility</a>)
is placed provocatively beside the leprechaun paraphernalia (146, 148, 149).
But it might just as easily be that the stamp through shield started their
lives at 146–149, contributing to a contiguous block of 11 treasures (140–150), before
inexplicably moving to 162–165 and having their places filled with leprechaun stuff.
(Of those 11 treasures, Donovan’s map clearly depicts only the shield; still,
the tusk, apple, tapestry, fleece, and perfume are not clearly absent.)</p>

<h2 id="surprises-in-the-recovered-long0501">Surprises in the recovered LONG0501</h2>

<p>The recovered version of LONG0501 (<code class="language-plaintext highlighter-rouge">&lt;Adventure--Experimental Version:5.0/6, NOV-78&gt;</code>)
has some intriguing differences from the
<a href="https://en.wikipedia.org/wiki/Most_recent_common_ancestor">most recent common ancestor</a>
of ANON0501, MCDO0551, ROBE0665, and LONG0751.</p>

<ul>
  <li>
    <p>The radium doesn’t exist yet; instead the trident is in the Bubble Chamber,
  rather than in the cavern with waterfall (as in WOOD0350) or on the east side of
  Blue Grotto (as in MCDO0551/LONG0751).
  I guess the Bubble Chamber’s punny name inspired the radium, rather than the
  other way around!</p>
  </li>
  <li>
    <p>The opal sphere doesn’t exist yet; instead the ruby slippers are in the Crystal Palace,
  rather than over the Rainbow (Room) as they are in ANON0501/MCDO0551/LONG0751.
  This is still thematic: the Crystal Palace is where the yellow sandstone path begins.
  Donovan’s map depicts them in the Rainbow Room itself, although this might be
  artistic license.</p>
  </li>
  <li>
    <p>The four-leafed clover doesn’t exist yet; instead the flowers are on the knoll,
  rather than at Ocean Vista.</p>
  </li>
  <li>
    <p>In <code class="language-plaintext highlighter-rouge">6.1/3</code>, the verb WEAR bypasses the burden-checking code so that you can WEAR
  something too heavy to GET. It seems that <code class="language-plaintext highlighter-rouge">5.0/6</code> has no such bug.</p>
  </li>
</ul>

<h2 id="an-unrelated-observation-on-state-changing-objects">An unrelated observation on state-changing objects</h2>

<p>In WOOD0350 and all its successors, whether the lamp is lighted or not is controlled
by changing the PROP value of the lamp. PROP values do not affect the short inventory
description of an object, only its long description when on the floor: both the lighted
and unlighted lamp are just “brass lantern.” If you want an
object’s inventory description to change, it needs to secretly be two items.
For example, the black bird (102) is rightly a different object from the
Maltese falcon (152), so as to give them two different inventory descriptions.</p>

<p>LONG0751’s tasty/spicey food is also implemented as two objects (188, 159).
Personally, if I had written the game, I would have made “spiceyness” simply a
PROP value; but the current behavior (two inventory descriptions) depends upon
its secretly being two objects.</p>

<p>The soiled paper is two objects (190, 191) despite both having the <em>same</em>
inventory description. I can’t think of a technical justification for that;
I think “valuableness” could easily have been encoded in the deed’s PROP value
instead, just like it is for the cask of wine and the Ming vase.</p>]]></content><author><name></name></author><category term="adventure" /><category term="digital-antiquaria" /><category term="rant" /><summary type="html"><![CDATA[Previously: “LONG0751 has been found” (no spoilers), “LONG0751: Mysteries of Donovan’s map” (minor spoilers). This post contains major spoilers for LONG0751!]]></summary></entry></feed>