<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Mick235711&apos;s Personal Website</title><description>Some interesting things I sometimes thought about.</description><link>https://mick235711.github.io/</link><language>en</language><item><title>Coroutine-Based Scope Guards</title><link>https://mick235711.github.io/2025/05/23/coroutine-scope-guards/</link><guid isPermaLink="true">https://mick235711.github.io/2025/05/23/coroutine-scope-guards/</guid><description>Implementing scope guards with C++ coroutines.</description><pubDate>Fri, 23 May 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The Python standard library provides a lot of convenience things, among which one is &lt;code&gt;contextlib.contextmanager&lt;/code&gt;, a decorator that allows turning any coroutine generator into a resource:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from contextlib import contextmanager

@contextmanager
def open_file(filename, mode=&quot;r&quot;):
    fp = open(filename, mode)
    try:
        yield fp
    finally:
        fp.close()

with open_file(&quot;test.txt&quot;) as fp:
    fp.write(&quot;Hello World!&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Anything before &lt;code&gt;yield&lt;/code&gt; will be treated as &lt;code&gt;__enter__&lt;/code&gt;, and anything after &lt;code&gt;yield&lt;/code&gt; will be treated as &lt;code&gt;__exit__&lt;/code&gt;, and with the help of &lt;code&gt;finally&lt;/code&gt; we implemented an always-called cleanup block regardless of the exit method (normally or by exception).&lt;/p&gt;
&lt;p&gt;In C++, we have RAII, which we can use to implement a scope guard:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;template&amp;lt;typename F&amp;gt;
struct scope_guard
{
    F f;
    scope_guard(F f) :f{f} {}
    ~scope_guard() { f(); }
};

{
    auto fp = fopen(&quot;test.txt&quot;, &quot;r&quot;);
    scope_guard _ = [fp] { fclose(fp); };
    // ... use fp ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, this requires a separation between entry and exit block, which seems less than ideal. In the Python example, the initialization and cleanup phases are nicely grouped together. Let&apos;s see if we can do the same thing in C++.&lt;/p&gt;
&lt;h1&gt;Bare Bones&lt;/h1&gt;
&lt;p&gt;Let&apos;s have an awaitable type that implements the Coroutine framework:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;template&amp;lt;class Resource&amp;gt;
class context
{
public:
    class promise_type;

    context(const context&amp;amp;) = delete;
    context(context&amp;amp;&amp;amp; other) noexcept
        : coroutine_{std::exchange(other.coroutine_, {})}
    {}
    context&amp;amp; operator=(this context&amp;amp; self, context other) noexcept
    {
        std::ranges::swap(self.coroutine_, other.coroutine_);
        return self;
    }

    ~context()
    {
        if (coroutine_ &amp;amp;&amp;amp; !coroutine_.done()) coroutine_.resume();
    }

private:
    std::coroutine_handle&amp;lt;promise_type&amp;gt; coroutine_ = nullptr;

    explicit context(std::coroutine_handle&amp;lt;promise_type&amp;gt; coro)
        : coroutine_{coro}
    {}
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Normal stuff, a move-only type that holds a coroutine handle to the promise type.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!NOTE]
The destructor need to resume the coroutine, since we want the cleanup code to run on destruction of the &lt;code&gt;context&lt;/code&gt; object.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Now entering the promise type, which contains a pointer to the managed resource, and embedding a noop final awaiter:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;template&amp;lt;class Resource&amp;gt;
class context&amp;lt;Resource&amp;gt;::promise_type
{
public:
    friend class context;

    context get_return_object(this promise_type&amp;amp; self) noexcept
    {
        return context{std::coroutine_handle&amp;lt;promise_type&amp;gt;::from_promise(self)};
    }

    static std::suspend_never initial_suspend() noexcept { return {}; }
    static std::suspend_never final_suspend() noexcept { return {}; }

    std::suspend_always yield_value(this promise_type&amp;amp; self, const Resource&amp;amp; val) noexcept
    {
        self.value_ = std::addressof(val);
        return {};
    }

    void await_transform() = delete;

    static void return_void() noexcept {}
    static void unhandled_exception() { throw; }

private:
    const Resource* value_ = nullptr;
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice that &lt;code&gt;initial_suspend&lt;/code&gt; returns &lt;code&gt;suspend_never&lt;/code&gt;, since we want the initialization code to run immediately after the construction of the &lt;code&gt;context&lt;/code&gt; object. Finally, some convenience method that access the stored value from the context:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const Resource&amp;amp; operator*(this const context&amp;amp; self)
{
    return *self.coroutine_.promise().value_;
}
const Resource&amp;amp; get(this const context&amp;amp; self) { return *self; }
const Resource* operator-&amp;gt;(this const context&amp;amp; self) { return &amp;amp;*self; }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With that, we have a working context manager:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// NOTE: pass-by-value to avoid coroutine dangling the reference
context&amp;lt;FILE*&amp;gt; open_file(std::string file_name)
{
    auto fp = fopen(file_name.c_str(), &quot;r&quot;);
    std::println(&quot;Opened file: {}&quot;, file_name);
    co_yield fp;
    fclose(fp);
    std::println(&quot;Closed file: {}&quot;, file_name);
}

void use()
{
    std::println(&quot;Entering block&quot;);
    {
        auto context = open_file(&quot;/tmp/test.txt&quot;);
        auto fp = *context;
        std::println(&quot;Get file fd: {}&quot;, fileno(fp));
    }
    std::println(&quot;Exiting block&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Calling &lt;code&gt;use()&lt;/code&gt; outputs:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Entering block
Opened file: /tmp/test.txt
Get file fd: 3
Closed file: /tmp/test.txt
Exiting block
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Great! We now have a way to bundle the initialization and cleanup code neatly in a function together.&lt;/p&gt;
&lt;h1&gt;Recursive Awaitable&lt;/h1&gt;
&lt;p&gt;The &lt;code&gt;get()&lt;/code&gt;/&lt;code&gt;operator*&lt;/code&gt; is still a pain to write; can we do better with &lt;code&gt;co_await&lt;/code&gt;ing the &lt;code&gt;context&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;On face value, it may seem impossible, since we essentially need to &lt;em&gt;reverse&lt;/em&gt; what &lt;code&gt;co_await&lt;/code&gt; usually do. Usually, we use &lt;code&gt;co_await&lt;/code&gt; to &lt;em&gt;await&lt;/em&gt; the finish of some async operations in the &lt;em&gt;inner&lt;/em&gt; function; but here, we want to execute the inner function first, and then await for the &lt;em&gt;outer&lt;/em&gt; function to finish, and finally run the rest of the inner function. It is like doing a &lt;code&gt;co_await&lt;/code&gt; from the inner function to the outside.&lt;/p&gt;
&lt;p&gt;Fortunately, C++20 Coroutines provides enough customization point to implement this reverse behavior. However, it is just factually impossible to execute the cleanup code at the end of the current block, as there is nothing to RAII on (the result of &lt;code&gt;co_await&lt;/code&gt; expression will be the resource itself for convenience). Thus, we need to execute the cleanup code (rest of the inner function) at the final suspension point of the outer function.&lt;/p&gt;
&lt;p&gt;Let&apos;s start by writing an awaiter:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;awaiter context::operator co_await(this context&amp;amp;&amp;amp; self) noexcept
{
    self.resume_ = false;
    return awaiter{self.coroutine_};
}

template&amp;lt;class Resource&amp;gt;
class context&amp;lt;Resource&amp;gt;::awaiter
{
public:
    friend class context;

    static bool await_ready() noexcept { return true; }
    static void await_suspend(std::coroutine_handle&amp;lt;&amp;gt;) noexcept {}
    const Resource&amp;amp; await_resume(this awaiter&amp;amp; self) noexcept
    {
        return *self.coroutine_.promise().value_;
    }

private:
    std::coroutine_handle&amp;lt;promise_type&amp;gt; coroutine_ = nullptr;

    explicit awaiter(std::coroutine_handle&amp;lt;promise_type&amp;gt; coro)
        : coroutine_{coro}
    {}
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nothing fancy here, just a normal awaiter storing the inner coroutine handle, and &lt;code&gt;await_resume()&lt;/code&gt; returns the stored value inside the inner coroutine handle&apos;s promise type. This value will then be used as the result of the &lt;code&gt;co_await&lt;/code&gt; expression, eliminating the need for &lt;code&gt;operator*&lt;/code&gt;/&lt;code&gt;get()&lt;/code&gt;. We don&apos;t need to do anything during suspension, so just let &lt;code&gt;await_ready()&lt;/code&gt; return &lt;code&gt;true&lt;/code&gt; to skip the suspension phase is ideal.&lt;/p&gt;
&lt;p&gt;To use &lt;code&gt;co_await&lt;/code&gt;, we still need an outer task type, which need to coordinate with the awaiter to store the inner coroutine handle:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class context_task
{
public:
    class promise_type
    {
    private:
        struct final_awaiter
        {
            static bool await_ready() noexcept { return false; }
            template &amp;lt;class Promise&amp;gt;
            static std::coroutine_handle&amp;lt;&amp;gt; await_suspend(std::coroutine_handle&amp;lt;Promise&amp;gt; coro) noexcept
            {
                // Symmetric transfer into the stored inner coroutine
                auto cont = coro.promise().continuation_;
                if (cont) return cont;
                return std::noop_coroutine();
            }
            static void await_resume() noexcept {}
        };

    public:
        context_task get_return_object(this promise_type&amp;amp; self) noexcept
        {
            return context_task{std::coroutine_handle&amp;lt;promise_type&amp;gt;::from_promise(self)};
        }
        static std::suspend_never initial_suspend() noexcept { return {}; }
        static final_awaiter final_suspend() noexcept { return {}; }
        static void return_void() noexcept {}
        static void unhandled_exception() { throw; }

        template&amp;lt;typename Resource&amp;gt;
        context&amp;lt;Resource&amp;gt;&amp;amp;&amp;amp; await_transform(this promise_type&amp;amp; self, context&amp;lt;Resource&amp;gt;&amp;amp;&amp;amp; ctx) noexcept
        {
            self.continuation_ = ctx.coroutine_;
            return std::move(ctx);
        }

    private:
        std::coroutine_handle&amp;lt;&amp;gt; continuation_ = nullptr;
    };

    context_task(const context_task&amp;amp;) = delete;
    context_task(context_task&amp;amp;&amp;amp; other) noexcept
        : coroutine_{std::exchange(other.coroutine_, {})}
    {}
    context_task&amp;amp; operator=(this context_task&amp;amp; self, context_task other) noexcept
    {
        std::ranges::swap(self.coroutine_, other.coroutine_);
        return self;
    }

    ~context_task()
    {
        if (coroutine_) coroutine_.destroy();
    }

private:
    std::coroutine_handle&amp;lt;promise_type&amp;gt; coroutine_ = nullptr;

    explicit context_task(std::coroutine_handle&amp;lt;promise_type&amp;gt; coro)
        : coroutine_{coro}
    {}
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Several things are notable for this outer task type. Apart from the normal move operation, destructor, and coroutine handle business that we see in every coroutine types, we also wrote a custom &lt;code&gt;final_awaiter&lt;/code&gt; to execute cleanup at the final suspension point, whose &lt;code&gt;await_suspend&lt;/code&gt; method will utilize &lt;a href=&quot;https://lewissbaker.github.io/2020/05/11/understanding_symmetric_transfer&quot;&gt;symmetric transfer&lt;/a&gt; to cheaply transfer to the inner coroutine to execute the cleanup code. This inner coroutine&apos;s handle is stored during the &lt;code&gt;await_transform&lt;/code&gt; call inside the &lt;code&gt;operator co_await&lt;/code&gt; machinery.&lt;/p&gt;
&lt;p&gt;With this new task type, we can use the context manager without needing to &lt;code&gt;get()&lt;/code&gt; anything:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;my::context_task use2()
{
    std::println(&quot;Entering block 2&quot;);
    {
        auto fp = co_await open_file(&quot;/tmp/test.txt&quot;);
        std::println(&quot;Get file fd: {}&quot;, fileno(fp));
    }
    std::println(&quot;Exiting block 2&quot;);
}

/*
Output:
Entering block 2
Opened file: /tmp/test.txt
Get file fd: 3
Exiting block 2
Closed file: /tmp/test.txt
*/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, &lt;code&gt;fp&lt;/code&gt; is already our stored resource type, neat! (Notice that the file is only closed after &lt;code&gt;use2()&lt;/code&gt; finishes, not at the end of the &lt;code&gt;fp&lt;/code&gt; scope; but this is acceptable for most usages.)&lt;/p&gt;
&lt;h1&gt;Error Handling&lt;/h1&gt;
&lt;p&gt;Of course, the above bare bones implementation ignores a lot of errors that might occur:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What will happen if the initialization code throws an exception or &lt;code&gt;co_return&lt;/code&gt;s early?&lt;/li&gt;
&lt;li&gt;What will happen if the cleanup code throws an exception?&lt;/li&gt;
&lt;li&gt;What will happen if the code &lt;code&gt;co_yield&lt;/code&gt;s zero times or more than one times?&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Premature Return&lt;/h3&gt;
&lt;p&gt;Our current code does not handle premature return at all; calling &lt;code&gt;co_return&lt;/code&gt; without doing any yielding will instantly crash the code.&lt;/p&gt;
&lt;h3&gt;Exception Handling&lt;/h3&gt;
&lt;p&gt;When exception occurs in the initialization part, it is fine; &lt;code&gt;throw;&lt;/code&gt; inside the &lt;code&gt;uncaught_exception()&lt;/code&gt; function will propagate that exception to the caller, who can handle it normally. The interesting case is when the cleanup portion throws an exception:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For the normal RAII case, exception is throw in &lt;code&gt;context&lt;/code&gt;&apos;s destructor (because that&apos;s where the inner coroutine is resumed), which will normally results in &lt;code&gt;std::terminate&lt;/code&gt;. To fix this you need to add &lt;code&gt;noexcept(false)&lt;/code&gt; to the destructor, and then you can handle the exception as if it is thrown at the end of the resource block.&lt;/li&gt;
&lt;li&gt;For the recursive await case, you can just handle the exception as if it is thrown at the closing brace of the outer function. This does mean that you need to handle it at the caller of the outer function.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Multiple Yields&lt;/h3&gt;
&lt;p&gt;Our current code also does not handle multiple &lt;code&gt;co_yield&lt;/code&gt;s; everything after the second &lt;code&gt;co_yield&lt;/code&gt; will be ignored as the coroutine handle will be destroyed after the second suspension (which is treated as final suspension, regardless of whether it really is final suspension).&lt;/p&gt;
&lt;p&gt;Handling of premature return and multiple yields is thus left as an exercise to the readers.&lt;/p&gt;
&lt;h1&gt;Performance&lt;/h1&gt;
&lt;p&gt;Well, there is no escape. This is C++, we care about performance. (If you don&apos;t, shouldn&apos;t you be down the road where there is a language that have this functionality built-in?)&lt;/p&gt;
&lt;p&gt;Let&apos;s put up Quick Bench and &lt;a href=&quot;https://quick-bench.com/q/De5P-3ahvHyzGV0JRl5V3oWImAQ&quot;&gt;see the results&lt;/a&gt;:
&amp;lt;img src=&quot;/upload/coroutine-scope-guard/quick-bench.png&quot; alt=&quot;Quick Bench Results&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;Well... not good. This is tested under Clang 17 + libstdc++ (-O3). Given that Clang optimize coroutines much better than GCC does, I&apos;d say this is the best result we can get.&lt;/p&gt;
&lt;p&gt;As perhaps expected, recursive await is a ~50% slowdown compared to normal RAII, and the latter is 23x slower than a simple scope guard. Well, you wouldn&apos;t use scope guards in a critical hot loop, anyway, right? Maybe it is fine, maybe not. This is just intended as a toy experiment, nothing more.&lt;/p&gt;
</content:encoded></item><item><title>Fun With Deducing This, SMFs and = delete</title><link>https://mick235711.github.io/2025/01/07/deducing-this-and-smf/</link><guid isPermaLink="true">https://mick235711.github.io/2025/01/07/deducing-this-and-smf/</guid><description>Exploring deducing this, special member functions, and implementation divergence.</description><pubDate>Tue, 07 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://wg21.link/P0847&quot;&gt;Deducing This&lt;/a&gt; is a new way of writing C++ member functions, which was introduced in C++23. This feature allows you to explicitly write the normally-implicit object argument (aka &lt;code&gt;this&lt;/code&gt;) in the argument list, just like Python&apos;s &lt;code&gt;self&lt;/code&gt; argument:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct S
{
    int value;
    void fun(int r) { value = r; } // normal member
    void fun2(this const S&amp;amp; self, int r) { self.value = r; } // deducing this
};

S s;
s.fun(4);
s.fun2(5); // usage is the same
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, the syntax for DT is to prepend &lt;code&gt;this&lt;/code&gt; on the first argument, which will be treated as the object argument that appears before &lt;code&gt;.&lt;/code&gt; or &lt;code&gt;-&amp;gt;&lt;/code&gt;. This is essentially a weakened form of Uniform Function-Call Syntax (UFCS), since DT essentially allows specifically-marked static non-member functions (as implemented behind the scenes) to be called with the member syntax.&lt;/p&gt;
&lt;p&gt;However, this post&apos;s purpose is not to explore the detail of Deducing This. Instead, it tries to answer a seemingly obvious question: can we write special member functions (SMFs) with Deducing This? If so, can they be &lt;code&gt;= default&lt;/code&gt;ed? This simple question have surprisingly non-trivial answers and incites several compiler bugs and inconsistent behavior across the board!&lt;/p&gt;
&lt;h1&gt;Terminology&lt;/h1&gt;
&lt;p&gt;Before we explore the interaction between Deducing This and SMFs, we must first clarify an often misunderstood term: what counts as special member functions?&lt;/p&gt;
&lt;h2&gt;Special Member Functions&lt;/h2&gt;
&lt;p&gt;Traditionally, special member functions refer to the functions that will be automatically declared by the compiler for a class, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Default constructors&lt;/li&gt;
&lt;li&gt;Copy constructors&lt;/li&gt;
&lt;li&gt;Move constructors&lt;/li&gt;
&lt;li&gt;Copy assignment operators&lt;/li&gt;
&lt;li&gt;Move assignment operators&lt;/li&gt;
&lt;li&gt;Prospective destructors&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;[!TIP]
The reason that all the constructors and operators are in plural form, and destructors is prepended by &quot;prospective&quot;, is because of C++20 Concepts. With &lt;code&gt;requires&lt;/code&gt; clauses, you can have several &quot;prospective&quot; destructors for a class, but only one will be available at any given time to act as the &quot;real&quot; destructor.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;These (except the default constructors) are also the functions affected by &lt;a href=&quot;https://mick235711.github.io/2024/04/30/operator-overloading-guide/#the-basics-the-rule-of-three-the-rule-of-five-and-the-rule-of-zero&quot;&gt;&quot;Rule of Five&quot;&lt;/a&gt;, which describes the customs and idioms related to defining those functions for a class (refer to the linked page for more information).&lt;/p&gt;
&lt;p&gt;Special Member Functions is a term &lt;a href=&quot;https://eel.is/c++draft/special#1&quot;&gt;defined by the standard&lt;/a&gt;, so the definition of them seems to be crystal clear, right?&lt;/p&gt;
&lt;p&gt;Not so fast! What is the &lt;em&gt;exact signature&lt;/em&gt; required for a constructor to be considered a SMF? (For example, is the &lt;a href=&quot;https://mick235711.github.io/2024/04/30/operator-overloading-guide/#copy-and-swap-idiom-when-and-how&quot;&gt;copy-and-swap&lt;/a&gt; assignment operator &lt;code&gt;X&amp;amp; operator=(X)&lt;/code&gt; considered copy assignment or move assignment?) What about default arguments? What about &lt;em&gt;template&lt;/em&gt;s? Nothing is &lt;em&gt;that&lt;/em&gt; simple in C++!&lt;/p&gt;
&lt;p&gt;Let&apos;s look at each special member function in detail.&lt;/p&gt;
&lt;h3&gt;Default Constructors and Destructors&lt;/h3&gt;
&lt;p&gt;This is the easiest case. A constructor is a default constructor if and only if:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Each parameter that is not a pack have a default argument.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That&apos;s it! (&lt;a href=&quot;https://eel.is/c++draft/class.default.ctor#1&quot;&gt;Standard&lt;/a&gt;) What this means essentially is that as long as the constructor &lt;em&gt;can&lt;/em&gt; be called with no arguments (&lt;code&gt;A()&lt;/code&gt;), it is a default constructor.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct A
{
    A(); // default constructor
    A(int x = 2, int y = 3); // also a default constructor
    template&amp;lt;typename T = int&amp;gt; A(T x = 2); // also a default constructor
    template&amp;lt;typename... Ts&amp;gt; A(Ts... args); // also a default constructor
    template&amp;lt;typename... Ts&amp;gt; A(); // also a default constructor

    A(int a, int b = 2); // not a default constructor
    template&amp;lt;typename T&amp;gt; A(); // also not
};
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;[!NOTE]
The access specifier, &lt;code&gt;noexcept&lt;/code&gt;, &lt;code&gt;explicit&lt;/code&gt;, &lt;code&gt;requires&lt;/code&gt;, or &lt;code&gt;constexpr&lt;/code&gt;/&lt;code&gt;consteval&lt;/code&gt; specifier will not affect whether a constructor is a SMF, same below.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Of course, the &lt;em&gt;implicitly generated&lt;/em&gt; default constructor (will be generated if no constructor is declared) always have the form:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;A() = default;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(the &lt;code&gt;constexpr&lt;/code&gt; and &lt;code&gt;noexcept&lt;/code&gt;-ness will be deduced by the member/base&apos;s default constructors; same below)&lt;/p&gt;
&lt;p&gt;A destructor for the class is a member declared with the &lt;code&gt;~A()&lt;/code&gt; syntax (with optional preceding specifier and &lt;code&gt;noexcept&lt;/code&gt;/&lt;code&gt;requires&lt;/code&gt;). It cannot be declared in any other form, so this is the only requirement. If no destructor and move operations is defined for a class, one will be implicitly generated with the form:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;~A() = default;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Copy/Move Constructors&lt;/h3&gt;
&lt;p&gt;Copy/Move constructors are called when an object is constructed by copying/moving another object. The standard &lt;a href=&quot;https://eel.is/c++draft/class.copy.ctor&quot;&gt;specified&lt;/a&gt; that a constructor for class &lt;code&gt;A&lt;/code&gt; will be identified as a copy/move constructor if and only if:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It is not a template.&lt;/li&gt;
&lt;li&gt;Its first parameter is &lt;code&gt;[cv] A&amp;amp;&lt;/code&gt; (for copy) / &lt;code&gt;[cv] A&amp;amp;&amp;amp;&lt;/code&gt; (for move).&lt;/li&gt;
&lt;li&gt;All non-first parameters have default arguments.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;[!NOTE]
&lt;code&gt;[cv]&lt;/code&gt; refers to any combinations of &lt;code&gt;const&lt;/code&gt; and &lt;code&gt;volatile&lt;/code&gt;, same below.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Again, this essentially means that the compiler will treat a constructor as SMF based on its callability with one argument, instead of its declared number of arguments.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct A
{
    A(const A&amp;amp;); // copy constructor
    A(A&amp;amp;); // also (used by auto_ptr&amp;lt;T&amp;gt; to indicate stole semantics)
    A(const volatile A&amp;amp;, int x = 2); // also

    A(A&amp;amp;&amp;amp;); // move constructor
    A(const A&amp;amp;&amp;amp;) // also (although very weird)
    A(volatile A&amp;amp;&amp;amp;, double x = 2.0); // also

    template&amp;lt;typename T = int&amp;gt;
    A(const A&amp;amp;); // not a copy constructor
    A(A&amp;amp;&amp;amp;, int x); // not a move constructor
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If no copy constructor is defined for a class, a copy constructor will be implicitly generated with the form&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;A(const A&amp;amp;) = default; // normal
A(A&amp;amp;) = default; // only if a subobject (member or base) have a copy constructor with argument [volatile] A&amp;amp;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If no copy/move operations and destructors are defined for a class, a move constructor will be implicitly generated with the form&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;A(A&amp;amp;&amp;amp;) = default;
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;[!NOTE]
A critical difference here is the criteria of implicit generation. If a move operation is declared, the copy constructor will still be generated; it will just be declared as &lt;code&gt;= delete&lt;/code&gt;. However, if a copy/move operation or a destructor is declared, the move constructor will not be generated at all, falling silently back to copying.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Copy/Move Assignment&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;[!TIP]
Note that &lt;code&gt;operator=&lt;/code&gt; can only be declared as a member function, so we don&apos;t need to deal with &lt;a href=&quot;https://mick235711.github.io/2024/04/30/operator-overloading-guide/#basics-of-operator-overloading&quot;&gt;operator overload form shenanigans&lt;/a&gt; here.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Copy/Move assignment are called when an object is assigned by lvalue/rvalue of the same type. The standard &lt;a href=&quot;https://eel.is/c++draft/class.copy.assign&quot;&gt;specified&lt;/a&gt; that a declared &lt;code&gt;operator=&lt;/code&gt; member function for class &lt;code&gt;A&lt;/code&gt; will be identified as a copy/move assignment if and only if:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It is not a template.&lt;/li&gt;
&lt;li&gt;Its first &lt;strong&gt;non-object&lt;/strong&gt; parameter is &lt;code&gt;A&lt;/code&gt; or &lt;code&gt;[cv] A&amp;amp;&lt;/code&gt; (for copy) / &lt;code&gt;[cv] A&amp;amp;&amp;amp;&lt;/code&gt; (for move).&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;[!NOTE]
Operator overloads, except for &lt;code&gt;operator()&lt;/code&gt; and &lt;code&gt;operator[]&lt;/code&gt;, cannot have default arguments, so that item does not apply here.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;struct A
{
    A&amp;amp; operator=(const A&amp;amp;); // copy assignment
    A&amp;amp; operator=(A&amp;amp;); // also (used by auto_ptr&amp;lt;T&amp;gt; to indicate stole semantics)
    int operator=(const volatile A&amp;amp;) const &amp;amp;; // also

    A&amp;amp; operator=(A&amp;amp;&amp;amp;) &amp;amp;; // move assignment
    double operator=(const A&amp;amp;&amp;amp;) const &amp;amp;&amp;amp;; // also (although very weird)

    template&amp;lt;typename T = int&amp;gt;
    A&amp;amp; operator=(const A&amp;amp;); // not a copy assignment
};
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;[!TIP]
Return types, &lt;code&gt;const&lt;/code&gt;, &lt;code&gt;volatile&lt;/code&gt;, and &lt;em&gt;ref-qualifier&lt;/em&gt;s also does not affect the validity of a copy/move assignment operator.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If no copy assignment operator is defined for a class, a copy assignment operator will be implicitly generated with the form&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;A&amp;amp; operator=(const A&amp;amp;) = default; // normal
A&amp;amp; operator=(A&amp;amp;) = default; // only if a subobject (member or base) have a copy assignment operator with non-object argument [volatile] A&amp;amp;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If no copy/move operations and destructors are defined for a class, a move assignment operator will be implicitly generated with the form&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;A&amp;amp; operator=(A&amp;amp;&amp;amp;) = default;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&lt;code&gt;= default&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Compared to SMFs which was a thing since the inception of C++, &lt;code&gt;= default&lt;/code&gt; is a relatively &quot;new&quot; (with 14 years of age already!) thing. Essentially, it requests the compiler to &quot;do as if this thing had been implicitly generated&quot;. You can explicitly request the default function body by using &lt;code&gt;= default&lt;/code&gt; &lt;em&gt;as&lt;/em&gt; the function body:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct A {}; // SMFs implicitly generated
struct B
{
    B() = default; // implemented as-if it is implicitly generated
    B&amp;amp; operator=(B&amp;amp;&amp;amp;) &amp;amp; = default; // implemented as-if it is implicitly generated
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Besides the textual benefit of writing out implicit functions explicitly, &lt;code&gt;= default&lt;/code&gt; also allows you to make small modifications to the implicit signatures of SMFs, as demonstrated by the use of &lt;em&gt;ref-qualifier&lt;/em&gt;s above. However, the possible modifications are &lt;a href=&quot;https://eel.is/c++draft/dcl.fct.def.default#2&quot;&gt;restricted&lt;/a&gt; by the standard explicitly. Only the following difference are permitted for &lt;code&gt;= default&lt;/code&gt; functions compared to the implicitly generated signatures:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;They may have different &lt;code&gt;noexcept&lt;/code&gt; specifications.&lt;/li&gt;
&lt;li&gt;For non-constructors, &lt;em&gt;ref-qualifier&lt;/em&gt;s can be different.&lt;/li&gt;
&lt;li&gt;If the implicit signature have a non-object parameter of type &lt;code&gt;const A&amp;amp;&lt;/code&gt;, the explicit signature can have a non-object parameter of type &lt;code&gt;A&amp;amp;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The explicit signature can be written in Deducing This, &lt;strong&gt;provided&lt;/strong&gt; that the type of the object parameter (the first one, prepended by &lt;code&gt;this&lt;/code&gt;) must also be a reference to &lt;code&gt;A&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;struct A
{
    A() noexcept = default; // fine, Rule 1
    A&amp;amp; operator=(const A&amp;amp;) noexcept &amp;amp; = default; // fine, Rule 1 + Rule 2
    A(A&amp;amp;) = default; // fine, Rule 3
    int operator=(const A&amp;amp;) = default; // error, not a permitted difference
    A(int x = 2) = default; // error, not a permitted difference
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this sense, functions that can be &lt;code&gt;= default&lt;/code&gt;ed can be said to be a more strictly restricted version of SMF signatures that are valid... or can they?&lt;/p&gt;
&lt;p&gt;Actually, these two sets are disjoint! Besides SMFs, there are other functions that can be &lt;code&gt;= default&lt;/code&gt;ed: comparison operators.&lt;/p&gt;
&lt;p&gt;The full story for comparison is too long to be described in this post, but interested readers can consult &lt;a href=&quot;https://mick235711.github.io/2024/04/30/operator-overloading-guide/#comparison-crash-course-operator-and-operator-and-other-five&quot;&gt;here&lt;/a&gt; for a detailed description. For this post, it is sufficient to note that&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A defaulted &lt;code&gt;&amp;lt;=&amp;gt;&lt;/code&gt; or &lt;code&gt;=&lt;/code&gt; (primary comparisons) means memberwise application of the operator.&lt;/li&gt;
&lt;li&gt;A defaulted other operator (secondary comparisons) means rewriting into one of primary comparison operators. For example, &lt;code&gt;a &amp;lt; b&lt;/code&gt; will default to rewriting into &lt;code&gt;(a &amp;lt;=&amp;gt; b) &amp;lt; 0&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;a href=&quot;https://eel.is/c++draft/class.compare.default&quot;&gt;criteria&lt;/a&gt; for a defaulted comparison operator, regardless of which operator is being declared, is as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It is not a template.&lt;/li&gt;
&lt;li&gt;It is either a non-static member function or a friend (non-member) function.&lt;/li&gt;
&lt;li&gt;Must have two (incl. explicit/implicit object parameter) parameters (this is restricted by the operator overload syntax) of the same type. The type must be &lt;code&gt;A&lt;/code&gt; or &lt;code&gt;const A&amp;amp;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Must return &lt;code&gt;bool&lt;/code&gt; if the operator is not &lt;code&gt;&amp;lt;=&amp;gt;&lt;/code&gt;. If declaring &lt;code&gt;&amp;lt;=&amp;gt;&lt;/code&gt;, the return type must either be &lt;code&gt;auto&lt;/code&gt; (exactly), a comparison category type, or a type that is convertible from all the &lt;code&gt;&amp;lt;=&amp;gt;&lt;/code&gt; result of subobjects.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;struct A
{
    int x;
    bool operator==(const A&amp;amp;) const = default; // fine, const A&amp;amp; + const A&amp;amp;
    bool operator&amp;lt;(const A&amp;amp;) = default; // error, first argument (implicit) is A&amp;amp;
    friend bool operator&amp;gt;(A, A) = default; // fine

    auto operator&amp;lt;=&amp;gt;(const A&amp;amp;) const = default; // fine
    friend std::any operator&amp;lt;=&amp;gt;(A, A) = default; // fine
    int operator&amp;lt;=&amp;gt;(const A&amp;amp;) const = default; // error, return type of x &amp;lt;=&amp;gt; x not convertible to int
};
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;[!CAUTION]
The fact that operators other than &lt;code&gt;&amp;lt;=&amp;gt;&lt;/code&gt; cannot use &lt;code&gt;auto&lt;/code&gt; in lieu of &lt;code&gt;bool&lt;/code&gt; or &lt;code&gt;auto&amp;amp;&lt;/code&gt; in lieu of &lt;code&gt;A&amp;amp;&lt;/code&gt; is inconsistent, and there is &lt;a href=&quot;https://wg21.link/P2952&quot;&gt;a proposal&lt;/a&gt; to fix that, so this behavior is likely to change in a future standard.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;Deducing This&lt;/h1&gt;
&lt;p&gt;Now onto the main part of this post: what does all of this have to do with Deducing This? Of course, since it is a new (or dare I say &lt;em&gt;better&lt;/em&gt;?) way of writing member functions, we should use it to write &lt;em&gt;special&lt;/em&gt; member functions!&lt;/p&gt;
&lt;h2&gt;What Does The Standard Say?&lt;/h2&gt;
&lt;p&gt;Surprisingly little at first! The author of DT seems to not consider the interaction with SMFs and comparison functions at all in the initial proposal, and thus the C++23 standard initially does not have any regulations regarding whether SMFs and comparison operators&apos;s validity when written in DT form.&lt;/p&gt;
&lt;p&gt;This omission was later identified, and resolved by the adoption of &lt;a href=&quot;https://wg21.link/CWG2586&quot;&gt;CWG 2586&lt;/a&gt;. Two key modification are made as a result of this issue:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The last rule regarding Deducing This is added to the &lt;code&gt;= default&lt;/code&gt; criteria above; and&lt;/li&gt;
&lt;li&gt;The &quot;two parameters&quot; in the comparison operator rule is clarified to mean two parameters &lt;strong&gt;including the explicit/implicit object parameter&lt;/strong&gt;. In other words, &lt;code&gt;bool operator==(const C&amp;amp;) const&lt;/code&gt; and &lt;code&gt;bool operator==(this const C&amp;amp;, const C&amp;amp;)&lt;/code&gt; both have two parameters of type &lt;code&gt;const C&amp;amp;&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;However, &lt;em&gt;standard&lt;/em&gt; is just a document, what does the &lt;em&gt;implementation&lt;/em&gt;s say about the matter?&lt;/p&gt;
&lt;h2&gt;Implementation Divergence&lt;/h2&gt;
&lt;p&gt;Let&apos;s see! (All results are obtained from the trunk versions of compilers as of January 2025)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!NOTE]
Since DT cannot be used on constructors or destructors, the only valid forms are on copy/move assignment operators and comparison operators.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;div class=&quot;table-scroll comparison-scroll&quot; tabindex=&quot;0&quot; aria-label=&quot;Scrollable implementation comparison table&quot;&amp;gt;
&amp;lt;table class=&quot;comparison smf-comparison&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&quot;text-align: left; white-space: nowrap; padding-right: 5px; width: var(--legendwidth);&quot;&amp;gt;
&amp;lt;col style=&quot;border-left: double; width: var(--launcherwidth);&quot;&amp;gt;
&amp;lt;col style=&quot;border-left: 1px solid lightgrey; width: var(--launcherwidth);&quot;&amp;gt;
&amp;lt;col style=&quot;border-left: 1px solid lightgrey; width: var(--launcherwidth);&quot;&amp;gt;
&amp;lt;col style=&quot;border-left: 1px solid lightgrey; width: var(--launcherwidth);&quot;&amp;gt;
&amp;lt;col style=&quot;border-left: 1px solid lightgrey; width: var(--launcherwidth);&quot;&amp;gt;
&amp;lt;col style=&quot;border-left: 1px solid lightgrey; width: var(--launcherwidth);&quot;&amp;gt;
&amp;lt;col style=&quot;border-left: 1px solid lightgrey; border-right: solid; width: var(--legendwidth);&quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;thead&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;Form&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;Standard&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;GCC&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;Clang&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;MSVC&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;EDG&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;Link&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;Comments&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;semititle line&quot; colspan=&quot;7&quot;&amp;gt;Copy Assignment&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;&amp;lt;code&amp;gt;A&amp;amp; operator=(this A&amp;amp;, const A&amp;amp;);&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;✅&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;✅&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;✅&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Welp, it seems that MSVC does not implement CWG 2586 at all...&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;✅&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;a href=&quot;https://godbolt.org/z/o51EserWq&quot;&amp;gt;Godbolt&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;Normal Copy Assignment&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;&amp;lt;code&amp;gt;A&amp;amp; operator=(this A&amp;amp;, A&amp;amp;);&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;✅&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;✅&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;✅&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Same as above, erroneously generate an implicit copy assignment operator and do resolution based on that&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;✅&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;a href=&quot;https://godbolt.org/z/hEh8z8GnE&quot;&amp;gt;Godbolt&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;Stealing Copy Assignment&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;&amp;lt;code&amp;gt;A&amp;amp; operator=(this A&amp;amp;, A);&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;✅&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;✅&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;✅&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Same as above, ambiguous between the implicitly generated one and CAS&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;✅&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;a href=&quot;https://godbolt.org/z/ra1r4jY9b&quot;&amp;gt;Godbolt&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;CAS Copy Assignment&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;&amp;lt;code&amp;gt;A&amp;amp; operator=(this const A&amp;amp;, const A&amp;amp;);&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot; style=&quot;z-index: 1000;&quot;&amp;gt;✅&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;There is a note in CWG 2586 that pointed out that it is weird for this to be considered a copy assignment; however as of now it is the status quo in the standard.&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Silently calls the implicitly generated one&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;✅&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Silently calls the implicitly generated one&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;?&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;a href=&quot;https://godbolt.org/z/WhYGooPE3&quot;&amp;gt;Godbolt&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;Copy Assignment With &amp;lt;code&amp;gt;const A&amp;amp;&amp;lt;/code&amp;gt; Object Param&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;&amp;lt;code&amp;gt;A&amp;amp; operator=(this A, const A&amp;amp;);&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;✅&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Ambiguous with the implicitly generated one&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;✅&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Ambiguous with the implicitly generated one&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;?&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;a href=&quot;https://godbolt.org/z/1bTbzdsPs&quot;&amp;gt;Godbolt&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;Copy Assignment With &amp;lt;code&amp;gt;A&amp;lt;/code&amp;gt; Object Param&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;&amp;lt;code&amp;gt;A&amp;amp; operator=(this int, const A&amp;amp;);&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;✅&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Silently calls the implicitly generated one&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;✅&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Silently calls the implicitly generated one&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Somehow generated an invalid redeclaration error&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;a href=&quot;https://godbolt.org/z/Mz7TeaGrx&quot;&amp;gt;Godbolt&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;Copy Assignment With Unrelated Object Param&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;&amp;lt;code&amp;gt;A&amp;amp; operator=(this auto&amp;amp;&amp;amp;, const A&amp;amp;);&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Must not be a template&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;❌&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;❌&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;❌&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;❌&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;a href=&quot;https://godbolt.org/z/9881PM6T8&quot;&amp;gt;Godbolt&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;Copy Assignment With Templated Object Param&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;&amp;lt;code&amp;gt;A&amp;amp; operator=(this A&amp;amp;, const A&amp;amp;) = default;&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;✅&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;✅&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;✅&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Currently it seems that MSVC just rejects defaulting functions with DT&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;EDG complains about signature only&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;a href=&quot;https://godbolt.org/z/aEvKhMejY&quot;&amp;gt;Godbolt&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot; rowspan=&quot;7&quot;&amp;gt;Above With &amp;lt;code&amp;gt;= default&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;&amp;lt;code&amp;gt;A&amp;amp; operator=(this A&amp;amp;, A&amp;amp;) = default;&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;✅&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;✅&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;✅&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;❌&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;❌&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;a href=&quot;https://godbolt.org/z/hjxMMzn8s&quot;&amp;gt;Godbolt&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;&amp;lt;code&amp;gt;A&amp;amp; operator=(this A&amp;amp;, A) = default;&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot; style=&quot;z-index: 1002;&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Not an allowed signature for defaulting&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;❌&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;❌&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Reject only because it cannot handle defaulting DT at all&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;❌&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;a href=&quot;https://godbolt.org/z/bY635q4cx&quot;&amp;gt;Godbolt&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;&amp;lt;code&amp;gt;A&amp;amp; operator=(this const A&amp;amp;, const A&amp;amp;) = default;&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot; style=&quot;z-index: 1001;&quot;&amp;gt;✅&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;The last rule for defaulting above specifies that any kind of reference to A is acceptable&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Ill-formed&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Default as deleted&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Ill-formed&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Ill-formed&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;a href=&quot;https://godbolt.org/z/8anavTsze&quot;&amp;gt;Godbolt&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;&amp;lt;code&amp;gt;A&amp;amp; operator=(this A, const A&amp;amp;) = default;&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot; style=&quot;z-index: 1000;&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Not a reference to A, which &amp;lt;a href=&quot;https://eel.is/c++draft/dcl.fct.def.default#2.5&quot;&amp;gt;should&amp;lt;/a&amp;gt; be default as deleted&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;almost tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Ill-formed&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Default as deleted&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;almost tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Ill-formed&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;almost tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Ill-formed&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;a href=&quot;https://godbolt.org/z/frrT8vhqj&quot;&amp;gt;Godbolt&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;&amp;lt;code&amp;gt;A&amp;amp; operator=(this int, const A&amp;amp;) = default;&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;❌&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;almost tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Ill-formed&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Default as deleted&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;almost tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Ill-formed&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;almost tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Ill-formed&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;a href=&quot;https://godbolt.org/z/WMKrW91ch&quot;&amp;gt;Godbolt&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;&amp;lt;code&amp;gt;A&amp;amp; operator=(this auto&amp;amp;&amp;amp;, const A&amp;amp;) = default;&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Must not be a template&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;❌&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;❌&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;❌&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;❌&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;a href=&quot;https://godbolt.org/z/EK9175Yv8&quot;&amp;gt;Godbolt&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;semititle line&quot; colspan=&quot;7&quot;&amp;gt;Move Assignment&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;&amp;lt;code&amp;gt;A&amp;amp; operator=(this A&amp;amp;, A&amp;amp;&amp;amp;);&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;✅&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;✅&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;✅&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Erroneously generate an implicit move assignment operator and do resolution based on that&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;✅&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;a href=&quot;https://godbolt.org/z/89TojWb7f&quot;&amp;gt;Godbolt&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;Normal Move Assignment&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;&amp;lt;code&amp;gt;A&amp;amp; operator=(this A&amp;amp;, const A&amp;amp;&amp;amp;);&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;✅&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;✅&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;✅&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;❌&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;✅&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;a href=&quot;https://godbolt.org/z/qh9G38xET&quot;&amp;gt;Godbolt&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;Weird Move Assignment&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;&amp;lt;code&amp;gt;A&amp;amp; operator=(this const A&amp;amp;, A&amp;amp;&amp;amp;);&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot; style=&quot;z-index: 1000;&quot;&amp;gt;✅&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;There is a note in CWG 2586 that pointed out that it is weird for this to be considered a move assignment; however as of now it is the status quo in the standard.&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Silently calls the implicitly generated one&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Ambiguous with the implicitly generated one&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Silently calls the implicitly generated one&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;✅&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;a href=&quot;https://godbolt.org/z/z71hfK9WT&quot;&amp;gt;Godbolt&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;Move Assignment With &amp;lt;code&amp;gt;const A&amp;amp;&amp;lt;/code&amp;gt; Object Param&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;&amp;lt;code&amp;gt;A&amp;amp; operator=(this A&amp;amp;&amp;amp;, A&amp;amp;&amp;amp;);&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;✅&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Ambiguous with the implicitly generated one&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;✅&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Ambiguous with the implicitly generated one&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;✅&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;a href=&quot;https://godbolt.org/z/a31jsj7WG&quot;&amp;gt;Godbolt&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;Move Assignment With &amp;lt;code&amp;gt;A&amp;amp;&amp;amp;&amp;lt;/code&amp;gt; Object Param&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;&amp;lt;code&amp;gt;A&amp;amp; operator=(this int, const A&amp;amp;);&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;✅&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Silently calls the implicitly generated one&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Conflicts with copy assignment&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Silently calls the implicitly generated one&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Conflicts with copy assignment&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;a href=&quot;https://godbolt.org/z/vz3vfP9rY&quot;&amp;gt;Godbolt&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;Move Assignment With Unrelated Object Param&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;&amp;lt;code&amp;gt;A&amp;amp; operator=(this auto&amp;amp;&amp;amp;, A&amp;amp;&amp;amp;);&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Must not be a template&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;❌&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;❌&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;❌&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;❌&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;a href=&quot;https://godbolt.org/z/eon5f8x9v&quot;&amp;gt;Godbolt&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;Move Assignment With Templated Object Param&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;&amp;lt;code&amp;gt;A&amp;amp; operator=(this A&amp;amp;, A&amp;amp;&amp;amp;) = default;&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;✅&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;✅&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;✅&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Currently it seems that MSVC just rejects defaulting functions with DT&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;EDG complains about signature only&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;a href=&quot;https://godbolt.org/z/fa13cqanz&quot;&amp;gt;Godbolt&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot; rowspan=&quot;6&quot;&amp;gt;Above With &amp;lt;code&amp;gt;= default&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;&amp;lt;code&amp;gt;A&amp;amp; operator=(this A&amp;amp;, const A&amp;amp;&amp;amp;) = default;&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot; style=&quot;z-index: 1002;&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Not a permitted deviation; only stripping const is allowed; &amp;lt;a href=&quot;https://eel.is/c++draft/dcl.fct.def.default#2.5&quot;&amp;gt;should&amp;lt;/a&amp;gt; be default as deleted&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Default as deleted&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Default as deleted&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;almost tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Ill-formed&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;almost tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Ill-formed&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;a href=&quot;https://godbolt.org/z/nPqnha7Pd&quot;&amp;gt;Godbolt&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;&amp;lt;code&amp;gt;A&amp;amp; operator=(this const A&amp;amp;, A&amp;amp;&amp;amp;) = default;&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot; style=&quot;z-index: 1001;&quot;&amp;gt;✅&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;The last rule for defaulting above specifies that any kind of reference to A is acceptable&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Ill-formed&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Default as deleted&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Ill-formed&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Ill-formed&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;a href=&quot;https://godbolt.org/z/rfxqPPa9n&quot;&amp;gt;Godbolt&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;&amp;lt;code&amp;gt;A&amp;amp; operator=(this A&amp;amp;&amp;amp;, A&amp;amp;&amp;amp;) = default;&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;✅&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;almost tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Ill-formed&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Default as deleted&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;almost tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Ill-formed&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;almost tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Ill-formed&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;a href=&quot;https://godbolt.org/z/zv9hKeb57&quot;&amp;gt;Godbolt&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;&amp;lt;code&amp;gt;A&amp;amp; operator=(this int, A&amp;amp;&amp;amp;) = default;&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;❌&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;almost tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Ill-formed&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Default as deleted&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;almost tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Ill-formed&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;almost tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Ill-formed&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;a href=&quot;https://godbolt.org/z/cja4WcsWY&quot;&amp;gt;Godbolt&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;&amp;lt;code&amp;gt;A&amp;amp; operator=(this auto&amp;amp;&amp;amp;, A&amp;amp;&amp;amp;) = default;&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Must not be a template&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;❌&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;❌&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;❌&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;❌&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;a href=&quot;https://godbolt.org/z/K49T9nK4q&quot;&amp;gt;Godbolt&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;semititle line&quot; colspan=&quot;7&quot;&amp;gt;Comparison&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;&amp;lt;code&amp;gt;auto operator&amp;lt;=&amp;gt;(this A, A) = default;&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;✅&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;✅&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;✅&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Refuse to recognize this as a comparison&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Refuse to default comparison written in DT&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;a href=&quot;https://godbolt.org/z/z45hcEMY7&quot;&amp;gt;Godbolt&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;Normal Spaceship With &amp;lt;code&amp;gt;A&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;&amp;lt;code&amp;gt;auto operator&amp;lt;=&amp;gt;(this const A&amp;amp;, const A&amp;amp;) = default;&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;✅&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;✅&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;✅&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;❌&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;❌&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;a href=&quot;https://godbolt.org/z/Mnfr1zW7f&quot;&amp;gt;Godbolt&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;Normal Spaceship With &amp;lt;code&amp;gt;const A&amp;amp;&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;&amp;lt;code&amp;gt;auto operator&amp;lt;=&amp;gt;(this const A&amp;amp;, A) = default;&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Two parameter must be of same type&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;❌&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;❌&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;❌&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;❌&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;a href=&quot;https://godbolt.org/z/he97vfb71&quot;&amp;gt;Godbolt&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;Asymmetric Spaceship&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;&amp;lt;code&amp;gt;auto operator&amp;lt;=&amp;gt;(this A&amp;amp;, A&amp;amp;) = default;&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Two parameter must be of either A or const A&amp;amp;&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;❌&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;❌&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;❌&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;❌&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;a href=&quot;https://godbolt.org/z/9jcYhax4z&quot;&amp;gt;Godbolt&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;Wrong Param Type Spaceship&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;&amp;lt;code&amp;gt;int operator&amp;lt;=&amp;gt;(this A, A) = default;&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Must return auto or a category type&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;✅&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;❌&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;❌&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;❌&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;a href=&quot;https://godbolt.org/z/b6aGWP7zK&quot;&amp;gt;Godbolt&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;Wrong Return Type Spaceship&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;&amp;lt;code&amp;gt;int operator==(this A, A) = default;&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Must return bool&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;❌&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;❌&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;❌&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;❌&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;a href=&quot;https://godbolt.org/z/66E7vfGM1&quot;&amp;gt;Godbolt&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;Wrong Return Type Equality&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;&amp;lt;code&amp;gt;auto operator&amp;lt;=&amp;gt;(this auto, A) = default;&amp;lt;/code&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot;&amp;gt;❌&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Must not be a template&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;❌&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;❌&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;❌&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;❌&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;a href=&quot;https://godbolt.org/z/v66s7jdvr&quot;&amp;gt;Godbolt&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;Templated Spaceship&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;Hmmm... Guess let&apos;s not use DT on SMFs for now if you want portability...&lt;/p&gt;
</content:encoded></item><item><title>Comparison of Minecraft Launchers</title><link>https://mick235711.github.io/2024/11/26/minecraft-launcher-comparison/</link><guid isPermaLink="true">https://mick235711.github.io/2024/11/26/minecraft-launcher-comparison/</guid><description>A detailed comparison of the major Minecraft launchers.</description><pubDate>Tue, 26 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Being a popular name, Minecraft have billions of players around the world. However, the official launcher really sucks, so there exists a plethora of unofficial, third-party launchers that blews the official one miles away in terms of design and functionality. In this post, I try to compare the functionality of the most popular Minecraft launchers/clients. Due to my inability to use all of the launchers in depth and the inherent subjectivity of the topic, I will not compare the design (aesthetics) and performance of different launchers, only their offered functionality.&lt;/p&gt;
&lt;h1&gt;Launcher Selection&lt;/h1&gt;
&lt;p&gt;As of Nov 2024, I think there are ten most popular launchers out there: (&lt;em&gt;italics&lt;/em&gt; is the one-line summary that exists on each launcher&apos;s official website; these are not my words but theirs.)&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.minecraft.net/en-us/download&quot;&gt;Minecraft Official Launcher&lt;/a&gt;. Well, the one and only, &lt;em&gt;officially supported&lt;/em&gt;, launcher. Even though its bad performance, poor functionality, and lack of customization is the root cause of these different third-party launchers&apos; existence, we still have to admit that this is the most used launcher, and the one that will be used by the beginners after buying the game.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Mod/Modpack Distribution Website Officials&lt;/strong&gt;:&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://modrinth.com/app&quot;&gt;Modrinth App&lt;/a&gt;. &lt;em&gt;The Modrinth App is a unique, open source launcher that allows you to play your favorite mods, and keep them up to date, all in one neat little package.&lt;/em&gt; Being the official launcher for the newly popularized mod distribution website Modrinth, this launcher have the best builtin integration with Modrinth mods and modpacks, and is often recommended for modders.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.curseforge.com/download/app&quot;&gt;CurseForge App&lt;/a&gt;. Despite recent controversy, CurseForge is still the oldest, most comprehensive mod distribution website, and its launcher has the best integration with CurseForge mod and modpacks, so this is still the go-to choice for many.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.feed-the-beast.com/ftb-app&quot;&gt;FTB App&lt;/a&gt;. Being the world&apos;s largest modpack distributor, Feed The Best&apos;s official launcher was, for a quite long time, the only launcher that can download FTB modpacks directly, and it remained a generally okay launcher even to this day with tight integration.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Internationally Popular&lt;/strong&gt;:&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://prismlauncher.org&quot;&gt;Prism Launcher&lt;/a&gt;. &lt;em&gt;An Open Source Minecraft launcher with the ability to manage multiple instances, accounts and mods. Focused on user freedom and free redistributability.&lt;/em&gt; This is a fork of PolyMC after one of its main author committed several controversy actions, and PolyMC is a fork of ManyMC, who is a fork of MultiMC. &lt;a href=&quot;https://multimc.org/&quot;&gt;MultiMC&lt;/a&gt; used to be the absolute best multi-instance launcher out there, but its development was abandoned in 2023, so multiple forks had emerged. In this post, for the entire MultiMC-series of launcher, I will just use Prism Launcher as a representative of all the MultiMC forks, since it is the most popular one.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://atlauncher.com/&quot;&gt;ATLauncher&lt;/a&gt;. &lt;em&gt;ATLauncher is a simple and easy to use Minecraft Launcher which contains 155 modpacks for you to choose from, as well as the ability to browse and install packs from other platforms including CurseForge, Modrinth and Technic.&lt;/em&gt; With built-in integration of many modpacks and download channels, this has become a recent favorite for many Minecraft modders.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://gdlauncher.com/&quot;&gt;GDLauncher&lt;/a&gt;. &lt;em&gt;GDLauncher is a simple, yet powerful Minecraft custom launcher with a strong focus on the user experience.&lt;/em&gt; With automatic downloads of mods and modpacks from different channels and a builtin Java version manager, this is also a favorite for many people.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Chinese Creation&lt;/strong&gt;: (Due to Netease&apos;s controversy takeover of Minecraft&apos;s distribution in China, many talented developer in China had developed fantastic third-party launchers for the international version of Minecraft, many exceeding the design and functionality provided by these mentioned above. However, a weakness is that these often have not-perfect English support.)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://hmcl.huangyuhui.net/&quot;&gt;HMCL (Hello Minecraft! Launcher)&lt;/a&gt;. &lt;em&gt;A Minecraft Launcher which is multi-functional, cross-platform and popular.&lt;/em&gt; Being one of the oldest launcher developed, it enjoyed unparalleled popularity in China, with many beginner&apos;s tutorial directly recommending this launcher. During its early days, pirated play was a focus, but currently it supports official login pretty well.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://afdian.com/a/LTCat&quot;&gt;PCL2 (Plain Craft Launcher 2)&lt;/a&gt;. A recently-emerged launcher with convenient, sleek UI, and gained popularity very quickly.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.bakaxl.com/&quot;&gt;BakaXL&lt;/a&gt;. &lt;em&gt;BakaXL is distinctive in born. Breaking out the layer concept used by classical launchers, BakaXL is more than satisfying to use. You can use the powerful custom theme feature without any additional purchase, with parallax effect and live wallpaper working together, which is amazing!&lt;/em&gt; Originally designed as a client for a specific server, it has since emerged to one of the best-looking launchers out there, with blazing fast speed and modern design (written with Rust + Tauri).&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;What, Your Favorite Launcher Is Not Here?&lt;/h2&gt;
&lt;p&gt;This guide does not include launchers that&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Only support pirated play of Minecraft. Please buy an official version, it is not expensive.&lt;/li&gt;
&lt;li&gt;Have stopped maintaining.&lt;/li&gt;
&lt;li&gt;Is a fork of one of the above.&lt;/li&gt;
&lt;li&gt;That does not let you create custom instances (such as Technic&apos;s official launcher).&lt;/li&gt;
&lt;li&gt;Have a limited user base.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The last one is subjective, but I really think these ten is a good representation of the most popular launchers in 2024. If you have any suggestions, feel free to &lt;a href=&quot;https://github.com/Mick235711/Mick235711.github.io/issues&quot;&gt;open an issue&lt;/a&gt; to add more launchers.&lt;/p&gt;
&lt;h1&gt;Comparison Table&lt;/h1&gt;
&lt;p&gt;This table only resembles the then-current functionality as of Nov 2024.&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;table-scroll comparison-scroll&quot; tabindex=&quot;0&quot; aria-label=&quot;Scrollable launcher comparison table&quot;&amp;gt;
&amp;lt;table class=&quot;comparison&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;colgroup&amp;gt;
&amp;lt;col style=&quot;text-align: left; white-space: nowrap; padding-right: 5px; width: var(--legendwidth);&quot;&amp;gt;
&amp;lt;col style=&quot;text-align: left; white-space: nowrap; padding-right: 5px; width: var(--legendwidth);&quot;&amp;gt; &amp;lt;!-- legend --&amp;gt;
&amp;lt;col style=&quot;border-left: double; width: var(--launcherwidth);&quot;&amp;gt;
&amp;lt;col style=&quot;border-left: 1px solid lightgrey; width: var(--launcherwidth);&quot;&amp;gt;
&amp;lt;col style=&quot;border-left: 1px solid lightgrey; width: var(--launcherwidth);&quot;&amp;gt;
&amp;lt;col style=&quot;border-left: 1px solid lightgrey; width: var(--launcherwidth);&quot;&amp;gt;
&amp;lt;col style=&quot;border-left: 1px solid lightgrey; width: var(--launcherwidth);&quot;&amp;gt;
&amp;lt;col style=&quot;border-left: 1px solid lightgrey; width: var(--launcherwidth);&quot;&amp;gt;
&amp;lt;col style=&quot;border-left: 1px solid lightgrey; width: var(--launcherwidth);&quot;&amp;gt;
&amp;lt;col style=&quot;border-left: 1px solid lightgrey; width: var(--launcherwidth);&quot;&amp;gt;
&amp;lt;col style=&quot;border-left: 1px solid lightgrey; width: var(--pclwidth);&quot;&amp;gt;
&amp;lt;col style=&quot;border-left: 1px solid lightgrey; border-right: solid; width: var(--bakaxlwidth);&quot;&amp;gt;
&amp;lt;/colgroup&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;thead&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot; rowspan=&quot;3&quot; colspan=&quot;2&quot;&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;Official&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot; colspan=&quot;3&quot;&amp;gt;Distribution Official&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot; colspan=&quot;3&quot;&amp;gt;International&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot; colspan=&quot;3&quot;&amp;gt;Chinese&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td rowspan=&quot;2&quot;&amp;gt;Minecraft Launcher&amp;lt;/td&amp;gt;
&amp;lt;td rowspan=&quot;2&quot;&amp;gt;Modrinth App&amp;lt;/td&amp;gt;
&amp;lt;td rowspan=&quot;2&quot;&amp;gt;CurseForge App&amp;lt;/td&amp;gt;
&amp;lt;td rowspan=&quot;2&quot;&amp;gt;FTB App&amp;lt;/td&amp;gt;
&amp;lt;td rowspan=&quot;2&quot;&amp;gt;Prism Launcher&amp;lt;/td&amp;gt;
&amp;lt;td rowspan=&quot;2&quot;&amp;gt;ATLauncher&amp;lt;/td&amp;gt;
&amp;lt;td rowspan=&quot;2&quot; class=&quot;tooltip&quot;&amp;gt;GDLauncher&amp;lt;span class=&quot;tooltiptext&quot; style=&quot;z-index: 1001;&quot;&amp;gt;This table focuses on the &amp;lt;a href=&quot;https://gdlauncher.com/docs/gdlauncher-vs-gdlauncher-carbon/&quot;&amp;gt;Carbon version&amp;lt;/a&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td rowspan=&quot;2&quot;&amp;gt;HMCL&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;PCL2&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;BakaXL&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td&amp;gt;
&amp;lt;table class=&quot;split&quot;&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td style=&quot;font-size: smaller;&quot;&amp;gt;Regular&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;tooltip&quot; style=&quot;font-size: smaller; border-right: 1px solid var(--line-divider); z-index: 1000;&quot;&amp;gt;CE&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;&amp;lt;a href=&quot;https://github.com/PCL-Community/PCL2-CE&quot;&amp;gt;Community Edition&amp;lt;/a&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;
&amp;lt;table class=&quot;split&quot;&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td style=&quot;font-size: smaller;&quot;&amp;gt;v3&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;tooltip&quot; style=&quot;font-size: smaller; border-right: 1px solid var(--line-divider);&quot;&amp;gt;v4&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Just &amp;lt;a href=&quot;https://www.bakaxl.com/v4&quot;&amp;gt;announced&amp;lt;/a&amp;gt;, no snapshot binary available yet&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;
&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/thead&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tbody&amp;gt;
&amp;lt;tr&amp;gt;
&amp;lt;td colspan=&quot;2&quot;&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;&amp;lt;a href=&quot;https://www.minecraft.net/en-us/download&quot;&amp;gt;&amp;lt;img class=&quot;logo&quot; src=&quot;/upload/minecraft-launcher-comparison/logos/official-launcher.webp&quot; /&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;&amp;lt;a href=&quot;https://modrinth.com/app&quot;&amp;gt;&amp;lt;img class=&quot;logo&quot; src=&quot;/upload/minecraft-launcher-comparison/logos/modrinth.avif&quot; /&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;&amp;lt;a href=&quot;https://www.curseforge.com/download/app&quot;&amp;gt;&amp;lt;img class=&quot;logo&quot; src=&quot;/upload/minecraft-launcher-comparison/logos/curseforge.png&quot; /&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;&amp;lt;a href=&quot;https://www.feed-the-beast.com/ftb-app&quot;&amp;gt;&amp;lt;img class=&quot;logo&quot; src=&quot;/upload/minecraft-launcher-comparison/logos/ftb.png&quot; /&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;&amp;lt;a href=&quot;https://prismlauncher.org&quot;&amp;gt;&amp;lt;img class=&quot;logo&quot; src=&quot;/upload/minecraft-launcher-comparison/logos/prism-launcher.png&quot; /&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;&amp;lt;a href=&quot;https://atlauncher.com/&quot;&amp;gt;&amp;lt;img class=&quot;logo&quot; src=&quot;/upload/minecraft-launcher-comparison/logos/atlauncher.svg&quot; /&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;&amp;lt;a href=&quot;https://gdlauncher.com/&quot;&amp;gt;&amp;lt;img class=&quot;logo&quot; src=&quot;/upload/minecraft-launcher-comparison/logos/gdlauncher.png&quot; /&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;&amp;lt;a href=&quot;https://hmcl.huangyuhui.net/&quot;&amp;gt;&amp;lt;img class=&quot;logo&quot; src=&quot;/upload/minecraft-launcher-comparison/logos/hmcl.ico&quot; /&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;&amp;lt;a href=&quot;https://afdian.com/a/LTCat&quot;&amp;gt;&amp;lt;img class=&quot;logo&quot; src=&quot;/upload/minecraft-launcher-comparison/logos/pcl2.png&quot; /&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;&amp;lt;a href=&quot;https://www.bakaxl.com/&quot;&amp;gt;&amp;lt;img class=&quot;logo&quot; src=&quot;/upload/minecraft-launcher-comparison/logos/bakaxl.png&quot; /&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td colspan=&quot;2&quot;&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;semititle line&quot; colspan=&quot;10&quot;&amp;gt;Development &amp;amp; Background&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot; colspan=&quot;2&quot;&amp;gt;Developer&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;Microsoft&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;Modrinth&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;Curse LLC&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;Feed The Beast&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;tooltip&quot;&amp;gt;Community&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;A &amp;lt;a href=&quot;https://prismlauncher.org/about/&quot;&amp;gt;group&amp;lt;/a&amp;gt; of 11 maintainers&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;Community&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;GorillaDevs&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;tooltip&quot;&amp;gt;Individual&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Made by huangyuhui&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;&amp;lt;table class=&quot;split&quot;&amp;gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;tooltip&quot;&amp;gt;Individual&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Made by LTCat (龙腾猫跃)&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;Community&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;tooltip&quot;&amp;gt;Individual&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Made by TT702&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot; colspan=&quot;2&quot;&amp;gt;Initial release&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;tooltip&quot;&amp;gt;2013&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;The new launcher (v2.x, v3.x) was released in Jul 2019&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;2023&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;2022&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;2022&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;tooltip&quot;&amp;gt;2022 (2014 for MultiMC)&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Prism Launcher&apos;s first forked version (v5.0) released in Oct 2022, before that it is under the name of PolyMC since Mar 2022, when it is itself forked from MultiMC&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;2016&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;2018&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;2015&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;&amp;lt;table class=&quot;split&quot;&amp;gt;&amp;lt;tr&amp;gt;
&amp;lt;td&amp;gt;2018&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;2024&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;2015&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot; colspan=&quot;2&quot;&amp;gt;Open Source&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;&amp;lt;a href=&quot;https://github.com/modrinth/code/blob/main/apps/app/README.md&quot;&amp;gt;Yes&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;&amp;lt;a href=&quot;https://github.com/FTBTeam/FTB-App&quot;&amp;gt;Yes&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;&amp;lt;a href=&quot;https://github.com/PrismLauncher/PrismLauncher&quot;&amp;gt;Yes&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;&amp;lt;a href=&quot;https://github.com/ATLauncher/ATLauncher&quot;&amp;gt;Yes&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;&amp;lt;a href=&quot;https://github.com/gorilla-devs/GDLauncher-Carbon&quot;&amp;gt;Yes&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;&amp;lt;a href=&quot;https://github.com/HMCL-dev/HMCL&quot;&amp;gt;Yes&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;table class=&quot;split&quot;&amp;gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;almost tooltip&quot;&amp;gt;&amp;lt;a href=&quot;https://github.com/Hex-Dragon/PCL2&quot;&amp;gt;Kind-of&amp;lt;/a&amp;gt;&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Source code repo is only updated after each stable release&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;&amp;lt;a href=&quot;https://github.com/PCL-Community/PCL2-CE&quot;&amp;gt;Yes&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot; colspan=&quot;2&quot;&amp;gt;License&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;N/A&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;GPL-3.0&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;N/A&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;LGPL-2.1&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;GPL-3.0&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;GPL-3.0&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;tooltip&quot;&amp;gt;&amp;lt;a href=&quot;https://github.com/gorilla-devs/GDLauncher-Carbon/blob/develop/LICENSE&quot;&amp;gt;Custom&amp;lt;/a&amp;gt;&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;The legacy version was GPL-3.0&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;GPL-3.0&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;&amp;lt;table class=&quot;split&quot;&amp;gt;&amp;lt;tr&amp;gt;
&amp;lt;td&amp;gt;&amp;lt;a href=&quot;https://github.com/Hex-Dragon/PCL2/blob/main/LICENCE&quot;&amp;gt;Custom&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;&amp;lt;a href=&quot;https://github.com/PCL-Community/PCL2-CE/blob/dev/LICENCE&quot;&amp;gt;Custom&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;N/A&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend tooltip&quot; colspan=&quot;2&quot;&amp;gt;Development Builds&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Including nightly, beta, ...&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;&amp;lt;a href=&quot;https://github.com/FTBTeam/FTB-App/tags&quot;&amp;gt;Yes&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot;&amp;gt;&amp;lt;a href=&quot;https://prismlauncher.org/wiki/development/development-builds/&quot;&amp;gt;Yes&amp;lt;/a&amp;gt;&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Per-commit build. Also available as -git packages&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;&amp;lt;a href=&quot;https://atl.pw/launcher-nightly&quot;&amp;gt;Yes&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;&amp;lt;a href=&quot;https://github.com/gorilla-devs/GDLauncher/releases&quot;&amp;gt;Yes&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;&amp;lt;a href=&quot;https://hmcl.huangyuhui.net/download/&quot;&amp;gt;Yes&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;table class=&quot;split&quot;&amp;gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;&amp;lt;a href=&quot;https://afdian.com/p/0164034c016c11ebafcb52540025c377&quot;&amp;gt;Paywalled&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;&amp;lt;a href=&quot;https://github.com/PCL-Community/PCL2-CE/releases&quot;&amp;gt;Yes&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;table class=&quot;split&quot;&amp;gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot;&amp;gt;&amp;lt;a href=&quot;http://jk-insider.bakaxl.com:8888/job/BakaXL%20Insider%20Parrot/lastSuccessfulBuild/&quot;&amp;gt;Yes&amp;lt;/a&amp;gt;&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Already in LTS, no new feature planned&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no tooltip&quot;&amp;gt;No&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Insider preview available for &amp;lt;a href=&quot;https://afdian.com/a/TT702&quot;&amp;gt;paid members&amp;lt;/a&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot; colspan=&quot;2&quot;&amp;gt;Core Language&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;C++&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;Rust&amp;lt;/td&amp;gt;
&amp;lt;td rowspan=&quot;2&quot;&amp;gt;Unknown&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;TypeScript&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;C++&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;Java&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;tooltip&quot;&amp;gt;Rust&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;The old version was written in JavaScript&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;Java&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;VB.NET&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;&amp;lt;table class=&quot;split&quot;&amp;gt;&amp;lt;tr&amp;gt;
&amp;lt;td&amp;gt;JavaScript&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;Rust&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend tooltip&quot; colspan=&quot;2&quot;&amp;gt;UI Framework&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Many also used Vue, which will not be listed&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;Chromium Embedded Framework&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;Tauri&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;Electron&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;Qt&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;Java Swing&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;tooltip&quot;&amp;gt;SolidJS&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;The old version was written in Electron&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;JavaFX&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;WPF&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;&amp;lt;table class=&quot;split&quot;&amp;gt;&amp;lt;tr&amp;gt;
&amp;lt;td&amp;gt;Electron&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;Tauri&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td colspan=&quot;2&quot;&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;semititle line&quot; colspan=&quot;10&quot;&amp;gt;Platform Support&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot; rowspan=&quot;3&quot;&amp;gt;Windows&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;64-bit&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend tooltip&quot; style=&quot;z-index: 1001;&quot;&amp;gt;32-bit&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Latest Minecraft version that supports 32-bit OS is 1.20.4&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no tooltip&quot;&amp;gt;No&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;MultiMC has 32-bit support&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;almost tooltip&quot;&amp;gt;Should work&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;JAR file provided&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;table class=&quot;split&quot;&amp;gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;almost tooltip&quot;&amp;gt;Yes&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;32-bit support works, but &amp;lt;a href=&quot;https://github.com/Hex-Dragon/PCL2/issues/3649&quot;&amp;gt;no maintenance work is planned&amp;lt;/a&amp;gt;. Bugfix reports will be ignored&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend tooltip&quot; style=&quot;z-index: 1000;&quot;&amp;gt;ARM 64-bit&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Officially supported only after 1.19&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;almost tooltip&quot;&amp;gt;Should work&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;JAR file provided&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot;&amp;gt;Yes&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;HMCL extended Minecraft ARM support to 1.8&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;table class=&quot;split&quot;&amp;gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;&amp;lt;a href=&quot;https://github.com/Hex-Dragon/PCL2/issues/1960&quot;&amp;gt;No&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;table class=&quot;split&quot;&amp;gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot; rowspan=&quot;2&quot;&amp;gt;macOS&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;64-bit&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot; rowspan=&quot;2&quot;&amp;gt;Universal&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot; rowspan=&quot;2&quot;&amp;gt;Universal&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot; rowspan=&quot;2&quot;&amp;gt;Universal JAR&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot; rowspan=&quot;2&quot;&amp;gt;Universal&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot; rowspan=&quot;2&quot;&amp;gt;Universal JAR&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;HMCL extended Minecraft ARM support to 1.8 using Rosetta 2&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot; rowspan=&quot;5&quot;&amp;gt;&amp;lt;a href=&quot;https://github.com/Hex-Dragon/PCL2/issues/54&quot;&amp;gt;No&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;table class=&quot;split&quot;&amp;gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;Unknown&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend tooltip&quot; style=&quot;z-index: 1000;&quot;&amp;gt;ARM 64-bit&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Officially supported only after 1.19&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;almost&quot;&amp;gt;Rosetta 2&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;table class=&quot;split&quot;&amp;gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot; rowspan=&quot;2&quot;&amp;gt;Linux&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;64-bit&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;almost&quot;&amp;gt;DEB+TAR&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;DEB+RPM&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;DEB only&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;DEB+RPM&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;TAR only&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;DEB+RPM&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;AppImage only&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;JAR&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;table class=&quot;split&quot;&amp;gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend tooltip&quot; style=&quot;z-index: 1000;&quot;&amp;gt;ARM 64-bit&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Officially supported only after 1.19&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;DEB+RPM&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;Flatpak only&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;almost&quot;&amp;gt;JAR&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;JAR&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;table class=&quot;split&quot;&amp;gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;Unknown&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend tooltip&quot; colspan=&quot;2&quot;&amp;gt;Other&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;No official support&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;JAR may work&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot;&amp;gt;JAR&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;HMCL &amp;lt;a href=&quot;https://github.com/HMCL-dev/HMCL/blob/main/PLATFORM.md&quot;&amp;gt;supports&amp;lt;/a&amp;gt; ARM32, MIPS64el, RISC-V 64, LoongArch64, and FreeBSD&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td colspan=&quot;2&quot;&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;semititle line tooltip&quot; colspan=&quot;10&quot;&amp;gt;Distribution Channel&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Only Intel/AMD 64-bit distributions are considered&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend tooltip&quot; colspan=&quot;2&quot; style=&quot;z-index: 1000;&quot;&amp;gt;Portable&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;i.e. no setup and no dependency executable&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot;&amp;gt;Yes&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Windows only; other platform can use JAR&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot;&amp;gt;Yes&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Windows only; other platform can use JAR&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot; rowspan=&quot;4&quot;&amp;gt;Windows&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;Microsoft Store&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;&amp;lt;a href=&quot;https://www.xbox.com/en-SG/games/store/minecraft-launcher/9pgw18npbzv5?ocid=storeforweb&quot;&amp;gt;Yes&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;WinGet&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;&amp;lt;a href=&quot;https://winget.run/pkg/Mojang/MinecraftLauncher&quot;&amp;gt;Yes&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;&amp;lt;a href=&quot;https://winstall.app/apps/Modrinth.ModrinthApp&quot;&amp;gt;Yes&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;&amp;lt;a href=&quot;https://winget.run/pkg/Overwolf/CurseForge&quot;&amp;gt;Yes&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;&amp;lt;a href=&quot;https://winstall.app/apps/FTB.App&quot;&amp;gt;Yes&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;&amp;lt;a href=&quot;https://winget.run/pkg/PrismLauncher/PrismLauncher&quot;&amp;gt;Yes&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;&amp;lt;a href=&quot;https://winget.run/pkg/ATLauncher/ATLauncher&quot;&amp;gt;Yes&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;&amp;lt;a href=&quot;https://winget.run/pkg/GorillaDevs/GDLauncher&quot;&amp;gt;Yes&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;&amp;lt;a href=&quot;https://winget.run/pkg/huanghongxun/HelloMinecraftLauncher&quot;&amp;gt;Yes&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;Chocolatey&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;almost&quot;&amp;gt;&amp;lt;a href=&quot;https://community.chocolatey.org/packages/minecraft-launcher&quot;&amp;gt;Unofficial&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;almost&quot;&amp;gt;&amp;lt;a href=&quot;https://community.chocolatey.org/packages/ftb&quot;&amp;gt;Unofficial&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;almost&quot;&amp;gt;&amp;lt;a href=&quot;https://community.chocolatey.org/packages/prismlauncher&quot;&amp;gt;Unofficial&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;almost&quot;&amp;gt;&amp;lt;a href=&quot;https://community.chocolatey.org/packages/gdlauncher&quot;&amp;gt;Unofficial&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;Scoop&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot;&amp;gt;Yes&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;games/minecraft&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;Unofficial&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;Unofficial&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot;&amp;gt;Yes&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;games/prismlauncher[-git]&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;Unofficial&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;Unofficial&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;Unofficial&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;table class=&quot;split&quot;&amp;gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;Unofficial&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend primary-legend&quot;&amp;gt;macOS&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;Homebrew&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;&amp;lt;a href=&quot;https://formulae.brew.sh/cask/minecraft&quot;&amp;gt;Yes&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;&amp;lt;a href=&quot;https://formulae.brew.sh/cask/modrinth&quot;&amp;gt;Yes&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;&amp;lt;a href=&quot;https://formulae.brew.sh/cask/curseforge&quot;&amp;gt;Yes&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;&amp;lt;a href=&quot;https://formulae.brew.sh/cask/feed-the-beast&quot;&amp;gt;Yes&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;&amp;lt;a href=&quot;https://formulae.brew.sh/cask/prismlauncher&quot;&amp;gt;Yes&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;&amp;lt;a href=&quot;https://formulae.brew.sh/cask/atlauncher&quot;&amp;gt;Yes&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no tooltip&quot;&amp;gt;No&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;The &amp;lt;a href=&quot;https://formulae.brew.sh/cask/gdlauncher&quot;&amp;gt;legacy version&amp;lt;/a&amp;gt; is available&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;N/A&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;N/A&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot; rowspan=&quot;3&quot;&amp;gt;Linux Universal&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;Flathub&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;&amp;lt;a href=&quot;https://flathub.org/apps/com.mojang.Minecraft&quot;&amp;gt;Unofficial&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot;&amp;gt;&amp;lt;a href=&quot;https://flathub.org/apps/com.modrinth.ModrinthApp&quot;&amp;gt;Yes&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Although unverified, recommended by the official website&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;&amp;lt;a href=&quot;https://flathub.org/apps/org.prismlauncher.PrismLauncher&quot;&amp;gt;Yes&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;&amp;lt;a href=&quot;https://flathub.org/apps/com.atlauncher.ATLauncher&quot;&amp;gt;Yes&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;&amp;lt;a href=&quot;https://flathub.org/apps/io.gdevs.GDLauncher&quot;&amp;gt;Unofficial&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot; rowspan=&quot;3&quot;&amp;gt;N/A&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot; rowspan=&quot;3&quot;&amp;gt;N/A&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;AppImage&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;&amp;lt;a href=&quot;https://portable-linux-apps.github.io/apps/minecraft-launcher.html&quot;&amp;gt;AppMan&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;&amp;lt;a href=&quot;https://portable-linux-apps.github.io/apps/hmcl.html&quot;&amp;gt;AppMan&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;Snap&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;&amp;lt;a href=&quot;https://snapcraft.io/mc-installer&quot;&amp;gt;Unofficial&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;notreally&quot;&amp;gt;&amp;lt;a href=&quot;https://snapcraft.io/gdlauncher&quot;&amp;gt;Abandoned&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot; rowspan=&quot;10&quot;&amp;gt;Linux Distros&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;Alpine&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;almost&quot;&amp;gt;&amp;lt;a href=&quot;https://pkgs.alpinelinux.org/package/edge/community/x86_64/prismlauncher&quot;&amp;gt;Community&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot; rowspan=&quot;10&quot;&amp;gt;N/A&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot; rowspan=&quot;10&quot;&amp;gt;N/A&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend tooltip&quot; style=&quot;z-index: 1001;&quot;&amp;gt;Arch&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;incl. Manjaro&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;almost tooltip&quot; style=&quot;z-index: 1000;&quot;&amp;gt;&amp;lt;a href=&quot;https://aur.archlinux.org/packages/minecraft-launcher&quot;&amp;gt;AUR&amp;lt;/a&amp;gt;&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Officially recommended&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;&amp;lt;a href=&quot;https://aur.archlinux.org/packages/modrinth-app&quot;&amp;gt;AUR&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;&amp;lt;a href=&quot;https://aur.archlinux.org/packages/curseforge&quot;&amp;gt;AUR&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;&amp;lt;a href=&quot;https://aur.archlinux.org/packages/ftb-app&quot;&amp;gt;AUR&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;&amp;lt;a href=&quot;https://archlinux.org/packages/extra/x86_64/prismlauncher/&quot;&amp;gt;Yes&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;&amp;lt;a href=&quot;https://aur.archlinux.org/packages/atlauncher&quot;&amp;gt;AUR&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;&amp;lt;a href=&quot;https://aur.archlinux.org/packages/gdlauncher&quot;&amp;gt;AUR&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;&amp;lt;a href=&quot;https://aur.archlinux.org/packages/hmcl&quot;&amp;gt;AUR&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend tooltip&quot;&amp;gt;Fedora&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;incl. CentOS Stream/RHEL&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;&amp;lt;a href=&quot;https://github.com/terrapkg/packages/tree/frawhide/anda/games/minecraft-java&quot;&amp;gt;Terra&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;&amp;lt;a href=&quot;https://copr.fedorainfracloud.org/coprs/g3tchoo/prismlauncher/&quot;&amp;gt;COPR&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend tooltip&quot;&amp;gt;Debian&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;incl. Ubuntu&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;&amp;lt;a href=&quot;https://mpr.makedeb.org/packages/minecraft-launcher&quot;&amp;gt;MPR&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;&amp;lt;a href=&quot;https://mpr.makedeb.org/packages/prismlauncher&quot;&amp;gt;MPR&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;Pi OS&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;&amp;lt;a href=&quot;https://pi-apps.io/wiki/getting-started/apps-list/#minecraft-java-prism-launcher&quot;&amp;gt;Yes&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;&amp;lt;a href=&quot;https://pi-apps.io/wiki/getting-started/apps-list/#minecraft-java-gdlauncher&quot;&amp;gt;Yes&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;Gentoo&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;&amp;lt;a href=&quot;https://packages.gentoo.org/packages/games-action/minecraft-launcher&quot;&amp;gt;Yes&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;&amp;lt;a href=&quot;https://packages.gentoo.org/packages/games-action/prismlauncher&quot;&amp;gt;Yes&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;NixOS&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;&amp;lt;a href=&quot;https://search.nixos.org/packages?query=minecraft-launcher&quot;&amp;gt;Yes&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;&amp;lt;a href=&quot;https://search.nixos.org/packages?query=modrinth-app&quot;&amp;gt;Yes&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;&amp;lt;a href=&quot;https://search.nixos.org/packages?query=prismlauncher&quot;&amp;gt;Yes&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;&amp;lt;a href=&quot;https://search.nixos.org/packages?query=atlauncher&quot;&amp;gt;Yes&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;&amp;lt;a href=&quot;https://search.nixos.org/packages?query=hmcl&quot;&amp;gt;Yes&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend tooltip&quot;&amp;gt;openSUSE&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;incl. SLE&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;almost&quot;&amp;gt;&amp;lt;a href=&quot;https://build.opensuse.org/package/show/games/minecraft-launcher&quot;&amp;gt;Games&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;&amp;lt;a href=&quot;https://build.opensuse.org/package/show/home:getchoo/prismlauncher&quot;&amp;gt;OBS&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;&amp;lt;a href=&quot;https://build.opensuse.org/package/show/home:Psheng/HMCL&quot;&amp;gt;OBS&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;Slackware&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;&amp;lt;a href=&quot;https://slackbuilds.org/repository/15.0/games/PrismLauncher/&quot;&amp;gt;SlackBuilds&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;Void&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot;&amp;gt;Yes&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;PrismLauncher&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td colspan=&quot;2&quot;&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;semititle line&quot; colspan=&quot;10&quot;&amp;gt;Basics&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot; rowspan=&quot;6&quot;&amp;gt;Minecraft&amp;lt;br /&amp;gt;Versions&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;Releases&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;Snapshots&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend tooltip&quot; style=&quot;z-index: 1002;&quot;&amp;gt;Betas&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Dec 2010 (b1.0) - Sep 2011 (b1.8.1)&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend tooltip&quot; style=&quot;z-index: 1001;&quot;&amp;gt;Alphas&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;May 2009 (rd-132211) - Dec 2010 (a1.2.6)&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend tooltip&quot; style=&quot;z-index: 1000;&quot;&amp;gt;Experiments&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;e.g. 1.14-16 combat experiment, 1.18-19 experiment snapshot&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;Demo&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot; colspan=&quot;2&quot;&amp;gt;Version Release Notes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot; rowspan=&quot;4&quot;&amp;gt;Java&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;Bundled&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot;&amp;gt;Yes&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Bundled with Microsoft-built OpenJDK&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;notreally tooltip&quot;&amp;gt;Delegated&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Used the official launcher&apos;s Java&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;Auto Detect&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;Auto Download&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;almost tooltip&quot;&amp;gt;Yes&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Automatically downloads Adoptium JDK at startup, regardless of whether you have Java installed&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend tooltip&quot; style=&quot;z-index: 1000;&quot;&amp;gt;Auto Match&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;In general, it is recommended to use Java 8 for 1.16-, Java 17 for 1.17-1.20.4, and Java 21 for 1.20.5+&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td colspan=&quot;2&quot;&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;semititle line&quot; colspan=&quot;10&quot;&amp;gt;Accounts&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot; rowspan=&quot;3&quot;&amp;gt;Singleplayer&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;Microsoft&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;almost tooltip&quot;&amp;gt;Yes&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Requires connection code&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;almost tooltip&quot;&amp;gt;Yes&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Requires connection code&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;almost tooltip&quot;&amp;gt;Yes&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Requires connection code&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;almost tooltip&quot;&amp;gt;Yes&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Requires connection code&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;almost tooltip&quot;&amp;gt;Yes&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Requires connection code&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;Offline&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend tooltip&quot; style=&quot;z-index: 1001;&quot;&amp;gt;Pirated&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;i.e. offline without valid account&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed tooltip&quot;&amp;gt;In China&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Outside mainland China, the launcher will require a valid account before allowing offline play&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed tooltip&quot;&amp;gt;In China&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Outside mainland China, the launcher will require a valid account before allowing offline play&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed tooltip&quot;&amp;gt;In China&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Outside mainland China, the launcher will require a valid account before allowing offline play&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend tooltip&quot; colspan=&quot;2&quot; style=&quot;z-index: 1000;&quot;&amp;gt;Quick Play&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Only available for 1.20+&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;notreally tooltip&quot;&amp;gt;Somewhat&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Support a beta &quot;quick launch&quot; mode that skips the official launcher, but cannot directly join worlds&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot; colspan=&quot;2&quot;&amp;gt;Skin Management&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td colspan=&quot;2&quot;&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;semititle line&quot; colspan=&quot;10&quot;&amp;gt;Modding&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot; rowspan=&quot;7&quot;&amp;gt;Mod Loader&amp;lt;br /&amp;gt;Auto Install&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;Forge&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend tooltip&quot; style=&quot;z-index: 1005;&quot;&amp;gt;NeoForge&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Fork of Forge, only available for 1.20.1+&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;almost tooltip&quot;&amp;gt;Yes&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Crash/hang when unsupported version selected&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend tooltip&quot; style=&quot;z-index: 1004;&quot;&amp;gt;Fabric&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Only available for 1.14+&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;almost tooltip&quot;&amp;gt;Yes&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Crash/hang when unsupported version selected&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend tooltip&quot; style=&quot;z-index: 1003;&quot;&amp;gt;Legacy Fabric&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Fork of Fabric that supports version prior to 1.14&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend tooltip&quot; style=&quot;z-index: 1002;&quot;&amp;gt;Quilt&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Only available for 1.14+&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;almost tooltip&quot;&amp;gt;Yes&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Crash/hang when unsupported version selected&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend tooltip&quot; style=&quot;z-index: 1001;&quot;&amp;gt;LiteLoader&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Discontinued. Only available for 1.6.2-1.12.2&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;notreally tooltip&quot;&amp;gt;No&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Listed as &quot;unsupported yet&quot;&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend tooltip&quot; style=&quot;z-index: 1000;&quot;&amp;gt;OptiFine&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Not a mod loader, but essential for some shaders&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot; rowspan=&quot;2&quot;&amp;gt;Mod&amp;lt;br /&amp;gt;Download&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;Modrinth&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;CurseForge&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot;&amp;gt;Yes&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Use website opening to bypass blocked mods&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot;&amp;gt;Yes&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Use website opening to bypass blocked mods&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot;&amp;gt;Yes&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Bypass granted by CurseForge&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot; rowspan=&quot;2&quot;&amp;gt;Resource Pack&amp;lt;br /&amp;gt;Download&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;Modrinth&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;table class=&quot;split&quot;&amp;gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;CurseForge&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;table class=&quot;split&quot;&amp;gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot; rowspan=&quot;2&quot;&amp;gt;Shader Pack&amp;lt;br /&amp;gt;Download&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;Modrinth&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;table class=&quot;split&quot;&amp;gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;CurseForge&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;table class=&quot;split&quot;&amp;gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot; colspan=&quot;2&quot;&amp;gt;Mod Version Auto-Match&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot; rowspan=&quot;2&quot;&amp;gt;N/A&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot; colspan=&quot;2&quot;&amp;gt;Mod Dependency&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;almost tooltip&quot;&amp;gt;Install only&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Disable mods will not warn about dependency&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot;&amp;gt;Yes&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;&amp;lt;a href=&quot;https://github.com/PrismLauncher/PrismLauncher/pull/3738&quot;&amp;gt;Coming in v11&amp;lt;/a&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;almost tooltip&quot;&amp;gt;Install only&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Disable mods will not warn about dependency&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;almost tooltip&quot;&amp;gt;Install only&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Disable mods will not warn about dependency&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;almost tooltip&quot;&amp;gt;Install only&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Disable mods will not warn about dependency&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;almost tooltip&quot;&amp;gt;Install only&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Disable mods will not warn about dependency&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;almost tooltip&quot;&amp;gt;Install only&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Disable mods will not warn about dependency&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot; rowspan=&quot;2&quot;&amp;gt;Datapack Download&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;Modrinth&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;CurseForge&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot; colspan=&quot;2&quot;&amp;gt;CurseForge World Download&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot; rowspan=&quot;5&quot;&amp;gt;Modpack&amp;lt;br /&amp;gt;Download&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;Modrinth&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;CurseForge&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot;&amp;gt;Yes&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;URL / Select&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;FTB&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot;&amp;gt;Yes&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;&amp;lt;a href=&quot;https://github.com/PrismLauncher/PrismLauncher/pull/3559&quot;&amp;gt;Coming in v11&amp;lt;/a&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot;&amp;gt;Yes&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;FTB modpack downloading was restored in Nov 2024&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;Technic&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;ATLauncher&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;almost&quot;&amp;gt;Experimental&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot;&amp;gt;Yes&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;URL / ID / Select&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot; rowspan=&quot;2&quot;&amp;gt;Auto Update&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;Mod(pack)&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot; rowspan=&quot;2&quot;&amp;gt;N/A&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;Mod only&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot;&amp;gt;Resource/Shader&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no tooltip&quot;&amp;gt;No&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Updater ignores non-mods&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;N/A&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;N/A&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;table class=&quot;split&quot;&amp;gt;&amp;lt;tr&amp;gt;
&amp;lt;td&amp;gt;N/A&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;N/A&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td colspan=&quot;2&quot;&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;semititle line&quot; colspan=&quot;10&quot;&amp;gt;Functionality&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot; colspan=&quot;2&quot;&amp;gt;Ads&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot; colspan=&quot;2&quot;&amp;gt;Multi-Language Support&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;notreally tooltip&quot;&amp;gt;WIP&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Available in &amp;lt;a href=&quot;https://github.com/Hex-Dragon/PCL2/discussions/4580&quot;&amp;gt;development builds&amp;lt;/a&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot; colspan=&quot;2&quot;&amp;gt;In-Game Overlay&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot;&amp;gt;Yes&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;via Overwolf&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot;&amp;gt;Yes&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;via Overwolf&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed tooltip&quot;&amp;gt;Linux only&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;via MangoHUD&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot; colspan=&quot;2&quot;&amp;gt;Instance Separation&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;notreally tooltip&quot;&amp;gt;Manual&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Possible by manually selecting different directories&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot; colspan=&quot;2&quot;&amp;gt;Instance Grouping&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot;&amp;gt;Yes&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Via setting categories&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot;&amp;gt;Yes&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Via setting categories&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot;&amp;gt;Yes&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Via bookmark&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot; colspan=&quot;2&quot;&amp;gt;Instance Backup&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot; colspan=&quot;2&quot;&amp;gt;One-Click Instance Launch&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot;&amp;gt;Yes&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Can create shortcut for instances on desktop&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend tooltip&quot; colspan=&quot;2&quot; style=&quot;z-index: 1001;&quot;&amp;gt;Directory Opener&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Easy/one-click access to mods, shareds, ... folders&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend tooltip&quot; colspan=&quot;2&quot; style=&quot;z-index: 1000;&quot;&amp;gt;World Management&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;e.g. Rename without launch the game, MCEdit, ...&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;table class=&quot;split&quot;&amp;gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot; colspan=&quot;2&quot;&amp;gt;Screenshot Management&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;table class=&quot;split&quot;&amp;gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot; colspan=&quot;2&quot;&amp;gt;Proxy&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;almost&quot;&amp;gt;Beta&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot; colspan=&quot;2&quot;&amp;gt;Customized Font&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;Size only&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;table class=&quot;split&quot;&amp;gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;&amp;lt;a href=&quot;https://github.com/Hex-Dragon/PCL2/issues/366&quot;&amp;gt;No&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot; colspan=&quot;2&quot;&amp;gt;Themes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed tooltip&quot;&amp;gt;Preset&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Have preset light / dark / OLED themes, no custom theme support&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed tooltip&quot;&amp;gt;Paywalled&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Classic / Light is free, other themes need premium&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed tooltip&quot;&amp;gt;Preset&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Have 4 presets, no custom theme support&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td&amp;gt;&amp;lt;table class=&quot;split&quot;&amp;gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;Paywalled&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot; colspan=&quot;2&quot;&amp;gt;Background&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;Have Cat&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend tooltip&quot; colspan=&quot;2&quot;&amp;gt;Utilities&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Network Checker, ...&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td colspan=&quot;2&quot;&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;semititle line&quot; colspan=&quot;10&quot;&amp;gt;Advanced&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot; rowspan=&quot;7&quot;&amp;gt;Instance&amp;lt;br /&amp;gt;Import / Export&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;legend tooltip&quot;&amp;gt;Modrinth&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;mrpack&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;Import only&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;Import only&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;Import only&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend tooltip&quot;&amp;gt;CurseForge&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;ZIP&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed tooltip&quot;&amp;gt;Import only&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Can only import from CurseForge App&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;Import only&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot;&amp;gt;Yes&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;ZIP / URL&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot;&amp;gt;Yes&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;ZIP / URL&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot;&amp;gt;Yes&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;ZIP / URL&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;Import only&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;Import only&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;Import only&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend tooltip&quot;&amp;gt;FTB&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;share code&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend tooltip&quot;&amp;gt;Technic&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;ZIP&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;Import only&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend tooltip&quot;&amp;gt;MultiMC&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;ZIP&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed tooltip&quot;&amp;gt;Import only&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Can import instance from MultiMC / GDLauncher / ATLauncher&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;Export only&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;Import only&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend tooltip&quot;&amp;gt;ATLauncher&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;ZIP&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend tooltip&quot;&amp;gt;MCBBS&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;v2, ZIP&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot; colspan=&quot;2&quot;&amp;gt;Command Line Interface&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;&amp;lt;a href=&quot;https://minecraft.fandom.com/wiki/Minecraft_Launcher#Command_line_usage&quot;&amp;gt;Yes&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;&amp;lt;a href=&quot;https://prismlauncher.org/wiki/getting-started/command-line-interface/&quot;&amp;gt;Yes&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot; colspan=&quot;2&quot;&amp;gt;JVM Param / Memory Limit&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;almost tooltip&quot;&amp;gt;Yes&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;No memory limit support, possible by passing -Xmx/-Xms&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;almost tooltip&quot;&amp;gt;Yes&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Built-in memory limit support but no parameter support, possible through official launcher&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot; colspan=&quot;2&quot;&amp;gt;Environmental Variables&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend tooltip&quot; colspan=&quot;2&quot; style=&quot;z-index: 1000;&quot;&amp;gt;API&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Pastebin, Modrinth/CurseForge API key, ...&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes tooltip&quot;&amp;gt;Yes&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;MineTogether&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot; colspan=&quot;2&quot;&amp;gt;Log Console&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;notreally tooltip&quot;&amp;gt;Static&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Logs shown only on error&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot; colspan=&quot;2&quot;&amp;gt;Log Analyze&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend tooltip&quot; colspan=&quot;2&quot; style=&quot;z-index: 1000;&quot;&amp;gt;Window Settings&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;process priority, window title, ...&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed tooltip&quot;&amp;gt;Partial&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Supports fullscreen and width / height spec&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed tooltip&quot;&amp;gt;Partial&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Supports fullscreen and width / height spec&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed tooltip&quot;&amp;gt;Partial&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Supports fullscreen and width / height spec&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed tooltip&quot;&amp;gt;Partial&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Supports fullscreen and width/height spec&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend&quot; colspan=&quot;2&quot;&amp;gt;NBT Analyze&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;Yes&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;No&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td colspan=&quot;2&quot;&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;semititle line&quot; colspan=&quot;10&quot;&amp;gt;Summary&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;legend tooltip&quot; colspan=&quot;2&quot; style=&quot;z-index: 1000;&quot;&amp;gt;Total Score&amp;lt;span class=&quot;tooltiptext&quot;&amp;gt;Yes = 1, Almost = 0.8, Mixed = 0.5, Not really = 0.2, No = 0; Maximum Score: 100&amp;lt;/span&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;32.7&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;42.2&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;30.7&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;39.2&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;76.4&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;60.9&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;35.9&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;yes&quot;&amp;gt;55.6&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;table class=&quot;split&quot;&amp;gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;37.9&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;45.8&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;line&quot;&amp;gt;&amp;lt;table class=&quot;split&quot;&amp;gt;&amp;lt;tr&amp;gt;
&amp;lt;td class=&quot;no&quot;&amp;gt;34.5&amp;lt;/td&amp;gt;
&amp;lt;td class=&quot;mixed&quot;&amp;gt;37.5&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;/table&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;Notes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;This table does not consider multiplayer/server-related functionalities (server opening/management, hosting, ...) due to me being unfamiliar with servers. An equally comprehensive comparison table &lt;a href=&quot;https://github.com/TayouVR/MinecraftLauncherComparison&quot;&gt;here&lt;/a&gt; can be used for that.&lt;/li&gt;
&lt;li&gt;The BakaXL column contains only the information for v3 for now (except the announced mutli-platform support). After v4 released I will update this column.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Screenshots&lt;/h1&gt;
&lt;p&gt;Several screenshots, mostly from official websites, to give a sense on what the UI for each launcher looks like.&lt;/p&gt;
&lt;p&gt;&amp;lt;figure class=&quot;image screenshot&quot;&amp;gt;
&amp;lt;img src=&quot;/upload/minecraft-launcher-comparison/official-launcher.webp&quot; alt=&quot;Official Launcher&quot;&amp;gt;
&amp;lt;figcaption&amp;gt;Official Launcher&amp;lt;/figcaption&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;figure class=&quot;image screenshot&quot;&amp;gt;
&amp;lt;img src=&quot;/upload/minecraft-launcher-comparison/modrinth.webp&quot; alt=&quot;Modrinth App&quot;&amp;gt;
&amp;lt;figcaption&amp;gt;Modrinth App v0.8.9&amp;lt;/figcaption&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;figure class=&quot;image screenshot&quot;&amp;gt;
&amp;lt;img src=&quot;/upload/minecraft-launcher-comparison/curseforge.webp&quot; alt=&quot;Modrinth App&quot;&amp;gt;
&amp;lt;figcaption&amp;gt;CurseForge App v1.265.0&amp;lt;/figcaption&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;figure class=&quot;image screenshot&quot;&amp;gt;
&amp;lt;img src=&quot;/upload/minecraft-launcher-comparison/ftb.webp&quot; alt=&quot;FTB App&quot;&amp;gt;
&amp;lt;figcaption&amp;gt;FTB App v1.26.3&amp;lt;/figcaption&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;figure class=&quot;image screenshot&quot;&amp;gt;
&amp;lt;img src=&quot;/upload/minecraft-launcher-comparison/prism-launcher.webp&quot; alt=&quot;Prism Launcher&quot;&amp;gt;
&amp;lt;figcaption&amp;gt;Prism Launcher v9.1&amp;lt;/figcaption&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;figure class=&quot;image screenshot&quot;&amp;gt;
&amp;lt;img src=&quot;/upload/minecraft-launcher-comparison/atlauncher.webp&quot; alt=&quot;ATLauncher&quot;&amp;gt;
&amp;lt;figcaption&amp;gt;ATLauncher v3.4.38.0&amp;lt;/figcaption&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;figure class=&quot;image screenshot&quot;&amp;gt;
&amp;lt;img src=&quot;/upload/minecraft-launcher-comparison/gdlauncher.webp&quot; alt=&quot;GDLauncher&quot;&amp;gt;
&amp;lt;figcaption&amp;gt;GDLauncher v2.0.20&amp;lt;/figcaption&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;figure class=&quot;image screenshot&quot;&amp;gt;
&amp;lt;img src=&quot;/upload/minecraft-launcher-comparison/hmcl.png&quot; alt=&quot;HMCL&quot;&amp;gt;
&amp;lt;figcaption&amp;gt;HMCL v3.2.134&amp;lt;/figcaption&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;figure class=&quot;image screenshot&quot;&amp;gt;
&amp;lt;img src=&quot;/upload/minecraft-launcher-comparison/pcl2.jpg&quot; alt=&quot;PCL2&quot;&amp;gt;
&amp;lt;figcaption&amp;gt;PCL v2.8.9&amp;lt;/figcaption&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;figure class=&quot;image screenshot&quot;&amp;gt;
&amp;lt;img src=&quot;/upload/minecraft-launcher-comparison/bakaxl.png&quot; alt=&quot;BakaXL&quot;&amp;gt;
&amp;lt;figcaption&amp;gt;BakaXL v3.5.1.0&amp;lt;/figcaption&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;h1&gt;Star History Charts&lt;/h1&gt;
&lt;p&gt;Here is the GitHub star history graph for some of the launchers above:
&amp;lt;figure class=&quot;image star-count&quot;&amp;gt;
&amp;lt;a href=&quot;https://star-history.com/#modrinth/code&amp;amp;FTBTeam/FTB-App&amp;amp;MultiMC/Launcher&amp;amp;PrismLauncher/PrismLauncher&amp;amp;ATLauncher/ATLauncher&amp;amp;gorilla-devs/GDLauncher&amp;amp;HMCL-dev/HMCL&amp;amp;Hex-Dragon/PCL2&amp;amp;PCL-Community/PCL2-CE&amp;amp;BakaXL-Launcher/BakaXL&amp;amp;Date&quot;&amp;gt;
&amp;lt;img src=&quot;https://api.star-history.com/svg?repos=modrinth/code,FTBTeam/FTB-App,MultiMC/Launcher,PrismLauncher/PrismLauncher,ATLauncher/ATLauncher,gorilla-devs/GDLauncher,HMCL-dev/HMCL,Hex-Dragon/PCL2,PCL-Community/PCL2-CE,BakaXL-Launcher/BakaXL&amp;amp;type=Date&quot; alt=&quot;Star History Chart&quot;&amp;gt;
&amp;lt;/a&amp;gt;
&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;Notes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;PCL2 and BakaXL are not fully open-sourced, so their star counts are not representative.&lt;/li&gt;
&lt;li&gt;GDLauncher&apos;s star count is for the legacy version, the Carbon version is too new to get a representative count.&lt;/li&gt;
&lt;li&gt;MultiMC has stopped development after 2023, hence the difference in trend.&lt;/li&gt;
&lt;li&gt;Modrinth&apos;s repo contains both the code for the app and the entire website.&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>The Definitive Guide to Operator Overloading in C++</title><link>https://mick235711.github.io/2024/04/30/operator-overloading-guide/</link><guid isPermaLink="true">https://mick235711.github.io/2024/04/30/operator-overloading-guide/</guid><description>A detailed guide to C++ operator overloading, its design choices, and pitfalls.</description><pubDate>Tue, 30 Apr 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Operator overloading has always been one of the most integral parts that makes C++, well, &lt;em&gt;C++&lt;/em&gt;. From the nearly-regular overload of &lt;code&gt;operator=&lt;/code&gt; for copy and move assignments to the IOStream’s (mis)use of &lt;code&gt;operator&amp;lt;&amp;lt;&lt;/code&gt; and &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt; for I/O, which every C++ programmer learns on the first day of the class, no one can deny that without operator overloading, many common idioms and syntaxes we are already accustomed to will no longer be possible.&lt;/p&gt;
&lt;p&gt;Yet the topic of operator overloading has always been a complex one, with intricacies that are not easy to understand and explore, and confusion and arguments on the best way to overload operators have prevailed ever since C++98. Furthermore, to make matters worse, each edition of C++ tweaked more and more operators to make them more friendly and also added more and more novel operators that we can overload:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;C++11 introduced the overloadable &lt;code&gt;operator &quot;&quot;udl&lt;/code&gt; (User-Defined Literals, we will treat it as an operator in this article since its function name contains &lt;code&gt;operator&lt;/code&gt;) and also introduced &lt;code&gt;explicit&lt;/code&gt; conversion operators to obsolete the Safe Bool Idiom.&lt;/li&gt;
&lt;li&gt;C++20 introduced &lt;code&gt;operator&amp;lt;=&amp;gt;&lt;/code&gt; (the spaceship) to obsolete five of the six comparison operators while also introducing the confusingly complex &lt;code&gt;operator co_await&lt;/code&gt; that we can also overload.&lt;/li&gt;
&lt;li&gt;C++23 introduced a &lt;code&gt;static&lt;/code&gt; version of &lt;code&gt;operator()&lt;/code&gt; and &lt;code&gt;operator[]&lt;/code&gt; and made the latter N-arg overloadable, changing decades of customs and perceptions of those operators.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What are the most canonical forms of overloading each operator? What are the usual idioms and protocols you must follow? Most importantly, what idiom prevailed in the C++23-era world, and what idiom had been made obsolete? Even with several &lt;a href=&quot;https://stackoverflow.com/questions/4421706/what-are-the-basic-rules-and-idioms-for-operator-overloading&quot;&gt;excellent guides&lt;/a&gt; written on the topics, they are either too old or don’t cover every operator’s intricacies. This guide is meant to answer all of those questions once and for all.&lt;/p&gt;
&lt;p&gt;All of the contents will be based on the finalized C++23 standard.&lt;/p&gt;
&lt;h2&gt;Basic Terminology&lt;/h2&gt;
&lt;h3&gt;Basics of Operator Overloading&lt;/h3&gt;
&lt;p&gt;So, what is operator overloading? As its name suggests, operator overloading basically gives you a way of customizing the behavior of operators. However, it should be made clear that this is not a way to &lt;em&gt;change&lt;/em&gt; the meaning of operators like &lt;code&gt;1 + 2&lt;/code&gt;, but to &lt;em&gt;give&lt;/em&gt; meaning to the otherwise-meaningless expression like &lt;code&gt;p1 + p2&lt;/code&gt;, where &lt;code&gt;p1&lt;/code&gt; and &lt;code&gt;p2&lt;/code&gt; are objects of your custom class &lt;code&gt;Point&lt;/code&gt;. Since the compiler doesn’t know how to add two &lt;code&gt;Point&lt;/code&gt; objects, it simply refuses to compile unless you tell the compiler what to do by overloading the &lt;code&gt;+&lt;/code&gt; operator on &lt;code&gt;Point&lt;/code&gt;s.&lt;/p&gt;
&lt;p&gt;So, how do you tell the compiler? For any operator you want to overload, say &lt;code&gt;+&lt;/code&gt;, there are two different syntaxes to overload the operator: member and non-member.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct Point
{
    Point operator+(const Point&amp;amp; rhs) const { /* ... */ } // Member
    Point func(const Point&amp;amp; rhs) const;
};
Point operator+(const Point&amp;amp; lhs, const Point&amp;amp; rhs) { /* ... */ } // Non-member
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After specifying &lt;strong&gt;one&lt;/strong&gt; of those two forms, the compiler will then treat &lt;code&gt;p1 + p2&lt;/code&gt; as a pure syntactic sugar: If a member &lt;code&gt;operator+&lt;/code&gt; is found, &lt;code&gt;p1 + p2&lt;/code&gt; is rewritten into &lt;code&gt;p1.operator+(p2)&lt;/code&gt; and executed, and you can access &lt;code&gt;p1&lt;/code&gt; as &lt;code&gt;this&lt;/code&gt;, &lt;code&gt;p2&lt;/code&gt; as &lt;code&gt;rhs&lt;/code&gt; in the function body. If a non-member is found, then &lt;code&gt;p1 + p2&lt;/code&gt; is rewritten into &lt;code&gt;operator+(p1, p2)&lt;/code&gt;, and you can access &lt;code&gt;p1&lt;/code&gt; and &lt;code&gt;p2&lt;/code&gt; as &lt;code&gt;lhs&lt;/code&gt; and &lt;code&gt;rhs&lt;/code&gt;, respectively.&lt;/p&gt;
&lt;p&gt;One subtle thing to notice here is that &lt;code&gt;operator+&lt;/code&gt; is (in some sense) not a magic name. It is literally just a function name, appearing where typically function names are found. In fact, you can even call it like a regular function, so &lt;code&gt;p1.operator+(p2)&lt;/code&gt; is actually valid C++ code. Therefore, defining an operator overload is literally just defining a member or non-member function, and the only magic is the sugar of rewriting &lt;code&gt;p1 + p2&lt;/code&gt; into a function call form.&lt;/p&gt;
&lt;p&gt;Since &lt;code&gt;operator+&lt;/code&gt;, like &lt;code&gt;func&lt;/code&gt;, is a normal function, all the usual privileges and restrictions of member and non-member functions apply. For example, you can add &lt;code&gt;const&lt;/code&gt;, &lt;code&gt;volatile&lt;/code&gt;, &lt;code&gt;&amp;amp;&lt;/code&gt;, or any other valid qualifiers on regular member functions to the &lt;code&gt;operator&lt;/code&gt; functions. You can make them &lt;code&gt;templates&lt;/code&gt;, add &lt;code&gt;requires&lt;/code&gt;, etc. Also, you can overload &lt;code&gt;operator+&lt;/code&gt;, and the compiler will make an overload resolution as usual when dealing with &lt;code&gt;p1 + p2&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Of course, some requirements do exist on &lt;code&gt;operator&lt;/code&gt; functions, so they are not precisely equivalent to ordinary functions. These requirements are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Each operator has its arity and membership requirements, so a binary operator like &lt;code&gt;/&lt;/code&gt; can only be a function with two arguments. Writing &lt;code&gt;operator/(int, int, int)&lt;/code&gt; is a hard error.&lt;/li&gt;
&lt;li&gt;If implemented as a non-member, at least one argument must be of (possibly reference to) a type that is dependent on at least one user-defined type. This effectively means that you cannot simply change what &lt;code&gt;1 + 2&lt;/code&gt; means by overloading &lt;code&gt;operator+(int, int)&lt;/code&gt;; you can only overload operators for non-builtin types.&lt;/li&gt;
&lt;li&gt;Operator functions may not have default arguments, except for &lt;code&gt;operator()&lt;/code&gt; and &lt;code&gt;operator[]&lt;/code&gt;. (Also, all operators except those two are either unary or binary, so they must have one or two arguments.)&lt;/li&gt;
&lt;li&gt;If implemented as a member, operator functions may not be &lt;code&gt;static&lt;/code&gt;, again, except for &lt;code&gt;operator()&lt;/code&gt; and &lt;code&gt;operator[]&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;However, except for those minor requirements, they behave entirely like normal functions. Especially notice that there are absolutely no requirements on what the return type and argument types must be (except for &lt;code&gt;operator-&amp;gt;&lt;/code&gt;, but that will be covered in its own section later), so you can definitely write an &lt;code&gt;operator+&lt;/code&gt; that takes two &lt;code&gt;BigInts&lt;/code&gt; and returns a &lt;code&gt;std::string&lt;/code&gt;. Also, no requirements are put on the implementation of operator functions, so it is also possible to write an &lt;code&gt;operator+&lt;/code&gt; on your &lt;code&gt;BigInt&lt;/code&gt; class such that &lt;code&gt;a + b&lt;/code&gt; actually does subtraction.&lt;/p&gt;
&lt;p&gt;However, one thing needs to be especially remembered when doing operator overloading:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Just because you can do something doesn’t mean you should!&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In general, you &lt;strong&gt;really&lt;/strong&gt; shouldn’t write surprise operators like that. Each operator has its own established meaning and relationships, and you should &lt;strong&gt;really&lt;/strong&gt; respect them. &lt;code&gt;a + b&lt;/code&gt; should do what addition usually does for that class. In this sense, &lt;code&gt;std::string::operator+&lt;/code&gt; is actually a misuse since there is not really an established meaning of what adding two strings should do in a mathematical sense (the current semantics, concatenation, is not even communicative!). While you may argue that &lt;code&gt;+&lt;/code&gt; for string means concatenation is so widespread and so universally adopted that it can be carved out as an exception, just as in Java, where no general operator overloading is allowed, but &lt;code&gt;String&lt;/code&gt; still has &lt;code&gt;+&lt;/code&gt;. (Even though I would argue that, again, it is better provided as an ordinary function that &lt;a href=&quot;https://en.cppreference.com/w/cpp/string/basic_string/append&quot;&gt;can have richer meaning, like slicing the second string&lt;/a&gt;, and &lt;a href=&quot;https://reference.wolfram.com/language/ref/StringJoin.html&quot;&gt;there exist popular languages that chose not to abuse +&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;However, even if you made that argument, another classical misuse of operator overloading in the STL cannot be explained away: IOStream’s use of &lt;code&gt;&amp;lt;&amp;lt;&lt;/code&gt; and &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt; for input and output. There, the grounds are much weaker: there is no precedent, and other languages haven’t adopted these operators (even C++ itself had been moving away from &lt;code&gt;&amp;lt;&amp;lt;&lt;/code&gt; and &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt; by encouraging the use of &lt;code&gt;std::print&lt;/code&gt; family), and the original meaning of those operators (bitwise shift) has absolutely nothing to do with I/O. In retrospect, the decision to overload &lt;code&gt;&amp;lt;&amp;lt;&lt;/code&gt; and &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt; for IOStreams is probably just meant to be a demonstration of the power of operator overloading, just like &lt;code&gt;vector&amp;lt;bool&amp;gt;&lt;/code&gt;, deployed as an experiment. Well, we can only accept that STL can and has made many mistakes, many of them more severe than this, and move on with life.&lt;/p&gt;
&lt;p&gt;In conclusion, with the freedom of operator overloading, you can really do extraordinary things, like C++20 Ranges’ use of &lt;code&gt;|&lt;/code&gt;, and DSLs like &lt;a href=&quot;http://boost-spirit.com/home/&quot;&gt;Boost::Spirit&lt;/a&gt;. However, before wielding that power, be cautious and always follow those &lt;a href=&quot;https://stackoverflow.com/a/4421708/6593187&quot;&gt;established guidelines&lt;/a&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Don’t do it.&lt;/strong&gt; In 95% of the cases, you don’t need operator overloading and just want to show off. In the vast majority of cases, introducing a named regular function can express the meaning more clearly, give you more power (such as the possibility of having more arguments), and bring less confusion. So, unless you have &lt;strong&gt;very clear and robust motivation&lt;/strong&gt;, refrain from overloading any operators.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Whenever the meaning of an operator is not obviously clear and undisputed, it should not be overloaded.&lt;/strong&gt; For example, what should &lt;code&gt;+&lt;/code&gt; between &lt;code&gt;vector&lt;/code&gt;s mean? You may be tempted to say concatenation, with the precedence of &lt;code&gt;string&lt;/code&gt;, but there is certainly no strong motivation or clear, established meaning on that operator (for example, it can also mean element-wise addition, just like what &lt;code&gt;valarray&lt;/code&gt; and &lt;code&gt;numpy.array&lt;/code&gt; does). Therefore, you shouldn’t overload it, even though it might be tempting. Instead, provide a named function, just like &lt;a href=&quot;https://en.cppreference.com/w/cpp/container/vector/append_range&quot;&gt;what STL eventually does&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Always stick to the operator’s well-known semantics.&lt;/strong&gt; This is an extension of 2, but it’s so important that it merits mentioning again. Don’t use “surprise operators” that make &lt;code&gt;a + b&lt;/code&gt; do subtraction; you will only confuse your users.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Always provide all out of a set of related operations.&lt;/strong&gt; This is a more subtle one, but nonetheless, it is still essential to follow. If the user can do &lt;code&gt;a &amp;lt; b&lt;/code&gt;, they will expect that they can do &lt;code&gt;a &amp;gt; b&lt;/code&gt;. Even though the compiler does not forbid you to write a class that only supports &lt;code&gt;&amp;lt;&lt;/code&gt;, you should always provide the full set, &lt;strong&gt;and make sure their behavior is consistent&lt;/strong&gt; (i.e., &lt;code&gt;a &amp;lt; b&lt;/code&gt; whenever &lt;code&gt;b &amp;gt; a&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Just because you can do something doesn’t mean you should!&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Transversing the Operator Zoo&lt;/h3&gt;
&lt;p&gt;Now that you know the basic syntax and guidelines of operator overloading, a natural question to ask is what operator we can overload. The first clarification here needed is that in C++, unlike some other languages, you cannot create new operators, so you cannot just write &lt;code&gt;operator**&lt;/code&gt; and expect the compiler to suddenly start accepting &lt;code&gt;a ** b&lt;/code&gt;. (Though, if you try really hard, you can make it work since &lt;code&gt;a ** b&lt;/code&gt; is parsed as &lt;code&gt;a * (*b)&lt;/code&gt;. But again, you really shouldn’t rely on those kind of tricks. How hard is providing a &lt;code&gt;pow()&lt;/code&gt;?)&lt;/p&gt;
&lt;p&gt;That leaves the already-usable operators. A thing to mention here is that not all operators in the core language consist only of punctuations; examples are &lt;code&gt;sizeof&lt;/code&gt; and &lt;code&gt;typeid&lt;/code&gt;, which are technically unary operators since they can apply to objects. However, all of those “text” operators cannot be overloaded, so we will not consider them operators in this guide. However, there is one notable exception: &lt;code&gt;swap&lt;/code&gt;. Even though &lt;code&gt;swap&lt;/code&gt; is not even a keyword and doesn’t really have any meaning in the core language, it is used and relied on so heavily in the standard library that I will make an exception and consider it as an overloadable binary “operator” in this guide. We will see the reason for this declaration more clearly in its section.&lt;/p&gt;
&lt;p&gt;Now that is resolved, among the remaining (“real”) operators, there are only four that cannot be overloaded: &lt;code&gt;.&lt;/code&gt; (object member access), &lt;code&gt;.*&lt;/code&gt; (object member access through pointers), &lt;code&gt;::&lt;/code&gt; (namespace access), and &lt;code&gt;?:&lt;/code&gt; (ternary/condition operator). Of those four “home-restricted”, their reasons are a little different. You obviously cannot overload &lt;code&gt;::&lt;/code&gt; due to the inability to pass a namespace name to a function, but there are not really any technical reasons for &lt;code&gt;?:&lt;/code&gt; not to be overloadable. The committee &lt;a href=&quot;https://isocpp.org/wiki/faq/operator-overloading#overload-dot&quot;&gt;admitted&lt;/a&gt; that the only reason &lt;code&gt;?:&lt;/code&gt; is not overloadable is that it is the only ternary operator in the standard, and he does not want to cave an exception for the allowance of a three-parameter &lt;code&gt;operator?:&lt;/code&gt; when all other operators are restricted to take one or two arguments (except &lt;code&gt;()&lt;/code&gt;, and later &lt;code&gt;[]&lt;/code&gt;, but they are unique in more than this respect, as seen above). Recently, there have been &lt;a href=&quot;https://wg21.link/P0917R3&quot;&gt;some attempts&lt;/a&gt; to persuade WG21 (the ISO C++ standards committee) to allow &lt;code&gt;operator?:&lt;/code&gt; in the context of natural SIMD conditionals that may benefit significantly from this operator.&lt;/p&gt;
&lt;p&gt;As for &lt;code&gt;.&lt;/code&gt; and &lt;code&gt;.*&lt;/code&gt;, the story is much more interesting and revealing. The urge to overload &lt;code&gt;operator.&lt;/code&gt;, the dot operator, to finally allow for a perfect wrapper class that can forward every method to an inner object (perhaps a perfect strong type alias or a locking guard that provides a lock for each method invocation), had been overwhelming in the last 20 years, and &lt;a href=&quot;https://wg21.link/N1671&quot;&gt;multiple&lt;/a&gt; &lt;a href=&quot;https://wg21.link/P0700R0&quot;&gt;proposals&lt;/a&gt; &lt;a href=&quot;https://wg21.link/P0252R2&quot;&gt;had&lt;/a&gt; been put forward to allow exactly that. However, this “smart reference” (&lt;em&gt;a la&lt;/em&gt; smart pointers) operator had been one of the most contentious topics in WG21 history due to issues like the clashing between the wrapper class’s own member function and &lt;code&gt;operator.&lt;/code&gt;, and whether &lt;code&gt;a + b&lt;/code&gt; should invoke &lt;code&gt;operator.&lt;/code&gt; on &lt;code&gt;a&lt;/code&gt; if it is translated to &lt;code&gt;a.operator+(b)&lt;/code&gt;, and so on. In the end, the topic has been left unresolved on the platform for a few years now (the most recent attempt seems to be in 2016).&lt;/p&gt;
&lt;p&gt;Committee shenanigans aside, except those four operators, C++ has allowed nearly every existing operator to be overloaded, including some surprising ones like &lt;code&gt;-&amp;gt;&lt;/code&gt; (pointer member access), &lt;code&gt;-&amp;gt;*&lt;/code&gt; (pointer member access through pointer), and &lt;code&gt;,&lt;/code&gt; (yes, you can overload comma!). Still, remember the motto!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Just because you can do something doesn’t mean you should!&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In total, C++ allows a staggering 39 different punctuation tokens to be overloaded, combined with non-punctuation overloadable, including &lt;code&gt;operator T&lt;/code&gt; (converting operator), &lt;code&gt;operator &quot;&quot;s&lt;/code&gt; (user-defined literal), &lt;code&gt;operator co_await&lt;/code&gt;, four allocating operators, and &lt;code&gt;swap&lt;/code&gt; (the only one in the list not using the &lt;code&gt;operator&lt;/code&gt; keyword), there are a total of 47 overloadable operators defined in the standard. (Now you know why this guide is so long, huh?) Grouping by their arity, we can classify them as three different kinds, which dictates their overloading syntax in terms of number of arguments allowed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Unary operators: &lt;code&gt;+&lt;/code&gt;, &lt;code&gt;-&lt;/code&gt;, &lt;code&gt;*&lt;/code&gt;, &lt;code&gt;&amp;amp;&lt;/code&gt;, &lt;code&gt;~&lt;/code&gt; (bitwise not), &lt;code&gt;!&lt;/code&gt;, &lt;code&gt;++&lt;/code&gt;, &lt;code&gt;--&lt;/code&gt;, &lt;code&gt;-&amp;gt;&lt;/code&gt;, &lt;code&gt;co_await&lt;/code&gt;, &lt;code&gt;operator T&lt;/code&gt;, &lt;code&gt;operator &quot;&quot;s&lt;/code&gt;, &lt;code&gt;new&lt;/code&gt;, &lt;code&gt;new[]&lt;/code&gt;, &lt;code&gt;delete&lt;/code&gt;, &lt;code&gt;delete[]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Binary operators: &lt;code&gt;+&lt;/code&gt;, &lt;code&gt;-&lt;/code&gt;, &lt;code&gt;*&lt;/code&gt;, &lt;code&gt;/&lt;/code&gt;, &lt;code&gt;%&lt;/code&gt;, &lt;code&gt;^&lt;/code&gt; (bitwise xor), &lt;code&gt;&amp;amp;&lt;/code&gt;, &lt;code&gt;|&lt;/code&gt;, &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;, &lt;code&gt;||&lt;/code&gt;, &lt;code&gt;&amp;lt;&amp;lt;&lt;/code&gt;, &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt;, &lt;code&gt;=&lt;/code&gt;, &lt;code&gt;+=&lt;/code&gt;, &lt;code&gt;-=&lt;/code&gt;, &lt;code&gt;*=&lt;/code&gt;, &lt;code&gt;/=&lt;/code&gt;, &lt;code&gt;%=&lt;/code&gt;, &lt;code&gt;^=&lt;/code&gt;, &lt;code&gt;&amp;amp;=&lt;/code&gt;, &lt;code&gt;|=&lt;/code&gt;, &lt;code&gt;&amp;lt;&amp;lt;=&lt;/code&gt;, &lt;code&gt;&amp;gt;&amp;gt;=&lt;/code&gt;, &lt;code&gt;==&lt;/code&gt;, &lt;code&gt;!=&lt;/code&gt;, &lt;code&gt;&amp;lt;&lt;/code&gt;, &lt;code&gt;&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;=&lt;/code&gt;, &lt;code&gt;&amp;gt;=&lt;/code&gt;, &lt;code&gt;&amp;lt;=&amp;gt;&lt;/code&gt;, &lt;code&gt;,&lt;/code&gt;, &lt;code&gt;-&amp;gt;*&lt;/code&gt;, &lt;code&gt;swap&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;N-ary operators: &lt;code&gt;()&lt;/code&gt; and &lt;code&gt;[]&lt;/code&gt; (again special, these can take any number of arguments, including zero)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Take focus on the fact that some tokens appear in multiple listings! In some cases, the two forms are linked; for example, unary &lt;code&gt;-&lt;/code&gt; is expected to do negation, so basically, &lt;code&gt;-x&lt;/code&gt; is equivalent to &lt;code&gt;0 - x&lt;/code&gt;, which uses binary &lt;code&gt;-&lt;/code&gt;. In other cases, the two forms are completely unrelated, such as unary &lt;code&gt;*&lt;/code&gt; means pointer dereferencing, and binary &lt;code&gt;*&lt;/code&gt; indicates multiplication. In the rest of the guide, to distinguish two forms, I will use &lt;code&gt;u+&lt;/code&gt; &lt;code&gt;u-&lt;/code&gt; &lt;code&gt;u*&lt;/code&gt; &lt;code&gt;u&amp;amp;&lt;/code&gt; to refer to their unary forms, while &lt;code&gt;b+&lt;/code&gt; &lt;code&gt;b-&lt;/code&gt; &lt;code&gt;b*&lt;/code&gt; &lt;code&gt;b&amp;amp;&lt;/code&gt; will refer to their binary forms.&lt;/p&gt;
&lt;p&gt;Another subtlety is that two operators are secretly expanding inside the unary operator’s category! &lt;code&gt;++&lt;/code&gt; and &lt;code&gt;--&lt;/code&gt; have two forms: prefix and postfix (all other unary operators are only prefixes). This means that you can make &lt;code&gt;++a&lt;/code&gt; and &lt;code&gt;a++&lt;/code&gt; do entirely different things! (Once again, you really shouldn’t; these are expected to be equivalent except for their return value. &lt;strong&gt;Remember the motto.&lt;/strong&gt;) In the rest of the guide, if I want to distinguish them clearly, I will use &lt;code&gt;++p&lt;/code&gt; and &lt;code&gt;--p&lt;/code&gt; to refer to their prefix forms and &lt;code&gt;p++&lt;/code&gt; and &lt;code&gt;p--&lt;/code&gt; to refer to their postfix forms. As for how do you distinguish them in code when both are unary? Read their section to find out!&lt;/p&gt;
&lt;p&gt;(Finally, alert readers may point out that &lt;code&gt;-&amp;gt;&lt;/code&gt; should be a binary operator since it is used like &lt;code&gt;ptr-&amp;gt;member()&lt;/code&gt;. This is not a mistake; welcome to the weird world of Arrow! Read its section to find out why it is a unary operator and a bizarre one at that.)&lt;/p&gt;
&lt;h2&gt;Basic Idioms&lt;/h2&gt;
&lt;p&gt;Before we embark on the journey to survey every single operator’s canonical forms and rules, we need to know about some general idioms that apply to nearly every operator overloading function.&lt;/p&gt;
&lt;h3&gt;Deducing This: A Retrospective and A Mistake Unfixed&lt;/h3&gt;
&lt;p&gt;For example, how exactly do you write a member operator function?&lt;/p&gt;
&lt;p&gt;This may sound trivially nonsense, but it’s not. In C++23, a new way to write member functions, &lt;a href=&quot;https://wg21.link/P0847&quot;&gt;Deducing This&lt;/a&gt;, is introduced into the standard. Specifically, this feature allows you to explicitly write the normally-implicit object argument (aka &lt;code&gt;this&lt;/code&gt;) in the argument list, just like Python’s &lt;code&gt;self&lt;/code&gt; argument. The syntax is to prepend &lt;code&gt;this&lt;/code&gt; on the first argument:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct S
{
    int value;
    void fun(int r) { value = r; } // normal member
    void fun2(this const S&amp;amp; self, int r) { self.value = r; } // deducing this
};

S s;
s.fun(4);
s.fun2(5); // usage is the same
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Some non-obvious details regarding those kinds of “deducing this” member functions need to be resolved. First of all, implicit and explicit access to &lt;code&gt;this&lt;/code&gt; is disabled in those functions; you cannot just write &lt;code&gt;value&lt;/code&gt; or write &lt;code&gt;this-&amp;gt;value&lt;/code&gt; and expect it to work. Instead, you need to access the members via &lt;code&gt;self&lt;/code&gt; (notice that this name is just an argument name and can be anything, not just &lt;code&gt;self&lt;/code&gt;). Secondly, I actually sorta lied when saying Deducing This is a new way of writing member functions. The best way to understand this is to again think DT as a syntactic sugar for an equivalent function by deleting &lt;code&gt;this&lt;/code&gt; and prepending &lt;code&gt;static&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct S
{
    void fun(this const S&amp;amp; self, int r);
    // equivalent to:
    static void fun(const S&amp;amp; self, int r);
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, the compiler simply transforms &lt;code&gt;s.fun(5)&lt;/code&gt; to a call of &lt;code&gt;S::fun(s, 5)&lt;/code&gt; whenever the overload resolution selects a DT function. This makes sense since we don’t have a &lt;code&gt;this&lt;/code&gt; pointer inside the function, making it ABI equivalent to a static function with better performance.&lt;/p&gt;
&lt;p&gt;Now, what are the benefits of using DT, you may ask? At first glance, this new form just adds more keystrokes and reduces the convenience of implicit &lt;code&gt;this&lt;/code&gt;. However, there are three main advantages of using DT.&lt;/p&gt;
&lt;p&gt;First of all, since DT members are just equivalent to a static member function, there is no rule whatsoever as to what the first argument’s type must be. For normal member functions, the implicit object argument’s type can be &lt;code&gt;S&amp;amp;&lt;/code&gt;, &lt;code&gt;S&amp;amp;&amp;amp;&lt;/code&gt;, &lt;code&gt;const S&amp;amp;&lt;/code&gt;, or &lt;code&gt;const S&amp;amp;&amp;amp;&lt;/code&gt; depending on the cv- and ref-qualifier at the end of the declaration, but it must be a reference. There is no such requirement on DT member functions:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct S
{
    void fun(); // implicit object argument is S&amp;amp; (sorta, see below)
    void fun2() const; // implicit object argument is const S&amp;amp;

    void fun3(this S&amp;amp;); // equivalent (sorta, see below) to fun
    void fun4(this const S&amp;amp;); // equivalent to fun2

    void fun5(this S); // pass by value! impossible to write for normal members
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Passing the implicit object by value has many benefits, including better performance due to avoiding implicit pointer access when writing members for small classes like &lt;code&gt;string_view&lt;/code&gt; that fits in registers and the possibility of a simple &lt;code&gt;sorted()&lt;/code&gt;-like function that returns a modified version of self without modifying in-place.&lt;/p&gt;
&lt;p&gt;But more importantly, there is no reason why a (static or not) member function cannot be a template. What makes DT special? Its first argument can also be templated!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct S
{
    T value;
    T&amp;amp; fun() { return value; }
    const T&amp;amp; fun() const { return value; } // common overload set to serve both kinds of this

    template&amp;lt;typename U&amp;gt;
    auto&amp;amp; fun(this U&amp;amp;&amp;amp; self) { return self.value; } // only need to write once!
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Using a forwarding reference (sometimes in conjunction with &lt;code&gt;std::forward[_like]&lt;/code&gt;), we can collapse the two or four duplicate overloads needed to handle different &lt;code&gt;const&lt;/code&gt;-ness into one templated member, and the right overload will be instantiated when needed.&lt;/p&gt;
&lt;p&gt;The second important advantage of DT is the possibility of exposing the &lt;code&gt;this&lt;/code&gt; pointer in a lambda. Since lambdas are basically syntactic sugars for anonymous classes with an &lt;code&gt;operator()&lt;/code&gt; overload, we cannot normally use &lt;code&gt;this&lt;/code&gt; to refer to that anonymous class because of &lt;code&gt;this&lt;/code&gt;-related captures. However, with DT syntax, we now have a way to refer to the lambda object inside itself:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;auto fac = [](this auto fac, int n)
{ return n &amp;lt;= 1 ? 1 : n * fac(n - 1); }
fac(5); // 120
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Besides the obvious recursive lambda, this also enables us to write a better overloading lambda wrapper. See &lt;a href=&quot;https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0847r7.html#recursive-lambdas&quot;&gt;the original proposal&lt;/a&gt; for details.&lt;/p&gt;
&lt;p&gt;The third advantage is not actually mentioned in the proposal at all and is a very less-known fact of normal member functions in C++. It is so less known that the standard itself made mistakes in this aspect, and this advantage is actually very relevant to why using DT to overload operators is a good idea. What is the weird quirk, you may ask? Basically, the above text (and the standard)’s reference to the “normal, non-&lt;code&gt;const&lt;/code&gt; member function’s implicit object argument is of type &lt;code&gt;S&amp;amp;&lt;/code&gt;” is a lie. Only lvalues of type &lt;code&gt;S&lt;/code&gt; will be accepted for a normal function taking an &lt;code&gt;S&amp;amp;&lt;/code&gt; argument. However, for normal non-&lt;code&gt;const&lt;/code&gt; member functions, both lvalue and rvalues are accepted!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct S { void fun(); };
void fun2(S&amp;amp;);

int main()
{
    S s;
    s.fun(); // okay
    fun2(s); // okay
    S{}.fun(); // prvalue, okay
    fun2(S{}); // prvalue, error!
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You see, the implicit object argument of non-&lt;code&gt;const&lt;/code&gt; member functions is actually the first instance of a “universal reference” in C++ standard that can accept both lvalues and rvalues, introduced way before C++11 forwarding references are a thing. This quirk does not apply to &lt;code&gt;const&lt;/code&gt; member functions because a normal function with &lt;code&gt;const S&amp;amp;&lt;/code&gt; arguments can already accept both lvalues and rvalues, making them truly equivalent.&lt;/p&gt;
&lt;p&gt;What does this quirk have to do with operator overloading? Remember, &lt;strong&gt;operator functions are just normal functions with a special name&lt;/strong&gt;, so all the properties of a regular member function apply. This has two important implications:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Asymmetry of Two Forms&lt;/strong&gt;: In nearly all regards, the compiler treats an operator’s member and non-member forms equivalently; &lt;code&gt;a + b&lt;/code&gt; will search and make overload resolutions with both forms and rewrite accordingly. However, this quirk means that if you write &lt;code&gt;operator+=&lt;/code&gt; as a member (without defense, see below), it will accept rvalues as the left-hand operand, making &lt;code&gt;S{} += 2&lt;/code&gt; valid. That statement will be invalid if you implement &lt;code&gt;operator+=&lt;/code&gt; as a non-member.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rvalue Modification&lt;/strong&gt;: This might be somewhat obvious since &lt;code&gt;S{} += 2&lt;/code&gt;, or in general, modifying an rvalue, which is most likely a temporary expression, is usually not a great idea. The modification result is most likely discarded, so write &lt;code&gt;S{} + 2&lt;/code&gt; is probably clearer. The standard library itself made this mistake: all of its &lt;code&gt;operator=&lt;/code&gt;s are member functions without defense, so nonsense expressions like &lt;code&gt;std::string{} = std::string{}&lt;/code&gt; &lt;a href=&quot;https://godbolt.org/z/TcPaeT4vY&quot;&gt;are actually valid&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You may wonder who actually writes expressions like that, modifying clearly temporary values. Well, maybe not directly, but you should remember that misspelling &lt;code&gt;==&lt;/code&gt; as &lt;code&gt;=&lt;/code&gt; is a very common mistake:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;std::optional&amp;lt;int&amp;gt; getOptional();
int getInt();

if (getOptional() = 2) // oops, meant to be ==
if (getInt() = 2) // protected! compile error
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Built-in types like &lt;code&gt;int&lt;/code&gt; do not have overloaded operators, so &lt;code&gt;operator=&lt;/code&gt; on &lt;code&gt;int&lt;/code&gt;s does not accept rvalues as LHS, and the above mistake is actually protected. However, regarding &lt;code&gt;std::optional&lt;/code&gt; (or any other STL types), even though &lt;code&gt;getOptional()&lt;/code&gt; returns a prvalue, you can still write an assignment like that, and &lt;a href=&quot;https://godbolt.org/z/MMzTYKEf7&quot;&gt;all major compilers compile successfully, albeit with a warning&lt;/a&gt;. (Apparently, MSVC does not even warn about this…)&lt;/p&gt;
&lt;p&gt;Now, a defense against this quirk exists, which is the &lt;em&gt;ref-qualifier&lt;/em&gt; feature introduced in C++11. This feature allows you to append &lt;code&gt;&amp;amp;&lt;/code&gt; or &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; after a member function to constraint whether the function only accepts lvalue &lt;code&gt;this&lt;/code&gt; or rvalue &lt;code&gt;this&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct S
{
    void fun();
    void fun2() &amp;amp;;
    void fun3() &amp;amp;&amp;amp;;
};

S getS();

int main()
{
    S s;
    s.fun(); // okay
    s.fun2(); // lvalue, okay
    s.fun3(); // error!

    getS().fun(); // okay (quirk)
    getS().fun2(); // error! (good)
    getS().fun3(); // rvalue, okay
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This not only gives you a way to express a member function with implicit object argument as &lt;code&gt;S&amp;amp;&amp;amp;&lt;/code&gt;, but appending &lt;code&gt;&amp;amp;&lt;/code&gt; also gives you feature parity with a normal &lt;code&gt;S&amp;amp;&lt;/code&gt; argument. Now, &lt;code&gt;struct S { void fun() &amp;amp;; };&lt;/code&gt; is indeed equivalent to &lt;code&gt;void fun(S&amp;amp;);&lt;/code&gt;, minus calling syntax. (However, now &lt;code&gt;struct S { void fun() const &amp;amp;; };&lt;/code&gt; is again not equivalent to &lt;code&gt;void fun(const S&amp;amp;);&lt;/code&gt;, instead being a non-expressible “const true lvalue reference” that only accepts lvalue. Isn’t C++ &lt;em&gt;fantastic&lt;/em&gt;? 😜)&lt;/p&gt;
&lt;p&gt;Applying this feature to operator overloading, we can now guard against modifying rvalues, and achieving symmetry:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct S
{
    S&amp;amp; operator=(int) &amp;amp;; // &amp;lt;- notice the &amp;amp;
};
S s;
S getS();
s = 2; // okay
getS() = 2; // error, good
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Unfortunately, &lt;em&gt;ref-qualifier&lt;/em&gt;s is probably one of the least known features of C++11, with little to no adoption both inside and outside the STL. There had been &lt;a href=&quot;https://wg21.link/N2819&quot;&gt;a proposal&lt;/a&gt; in the C++11 cycle requesting WG21 to change all existing standard library types to use an &lt;code&gt;operator=&lt;/code&gt; with &lt;code&gt;&amp;amp;&lt;/code&gt; qualifier and eventually modify the automatic generation rules to force that as default. However, due to the sheer amount of breakage this may cause, without any surprise, that proposal is not accepted. To maintain consistency, new library types introduced after C++11 still haven’t adopted any &lt;code&gt;operator=&lt;/code&gt; with a qualifier, resulting in our unsatisfactory contemporary status.&lt;/p&gt;
&lt;p&gt;But now, we may have a cure for that disease: Deducing This. One of the main reasons &lt;code&gt;&amp;amp;&lt;/code&gt;-qualified members had not seen great adoption is due to its asymmetry: you have to remember to add &lt;code&gt;&amp;amp;&lt;/code&gt; for non-&lt;code&gt;const&lt;/code&gt; members, but also remember &lt;strong&gt;not&lt;/strong&gt; to add &lt;code&gt;&amp;amp;&lt;/code&gt; for &lt;code&gt;const&lt;/code&gt; members to achieve feature parity. However, DT has no such asymmetry: due to its equivalence with static functions, &lt;code&gt;void fun(this S&amp;amp;)&lt;/code&gt; is, so obviously, equivalent to normal &lt;code&gt;void fun(S&amp;amp;)&lt;/code&gt;, and &lt;code&gt;void fun(this const S&amp;amp;)&lt;/code&gt; is also just equivalent to normal &lt;code&gt;void fun(const S&amp;amp;)&lt;/code&gt;. Even better, since DT uses normal function declarations syntax, there is literally no way to write the quirky “universal reference” or “const true lvalue reference” in DT members, so there are no bad defaults here; you have to write out the type physically. By simply writing all (non-&lt;code&gt;virtual&lt;/code&gt;, for now) members (including operator overloads) in DT form, you already achieved the rvalue modification prevention goal without intentionally doing anything!&lt;/p&gt;
&lt;p&gt;So, in conclusion, for modifying operators that probably should be written as non-&lt;code&gt;const&lt;/code&gt; member functions (see below section for why), the canonical form is to either write it in Deducing This form or to append the &lt;code&gt;&amp;amp;&lt;/code&gt; qualifier. This way, both symmetry and prevention of rvalue modification can be achieved.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct S
{
    S&amp;amp; operator=(this S&amp;amp;, const S&amp;amp;); // canonical and preferred
    S&amp;amp; operator=(const S&amp;amp;) &amp;amp;; // canonical
    S&amp;amp; operator=(const S&amp;amp;); // not recommended
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(Of course, if your operator does want to allow modification on rvalues, you can write it as a normal member (or preferably a DT member with forwarding reference); maybe the Builder pattern’s &lt;code&gt;operator=&lt;/code&gt; can be one example.)&lt;/p&gt;
&lt;h3&gt;Hidden Friends and the Barton-Nackman Trick&lt;/h3&gt;
&lt;p&gt;Now that we know the canonical forms for member function implementation of operator overloading, what about the non-member implementation? There, no symmetry problem occurs since both arguments are treated equally. However, another problem arose, necessitating the introduction of another commonly used implementation technique of non-member functions: the Hidden Friend Idiom.&lt;/p&gt;
&lt;p&gt;To understand hidden friends, we first must understand a &lt;code&gt;friend&lt;/code&gt; declaration. Traditionally, &lt;code&gt;friend&lt;/code&gt; declarations are used to intentionally loosen a class’s encapsulation in a controlled manner. For example, you may have a CRTP base class that you want to access some private method to aid implementation, which you do not want to expose to the outside world:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;template&amp;lt;typename Derived&amp;gt;
struct provide_work
{
    void work() { static_cast&amp;lt;Derived*&amp;gt;(this)-&amp;gt;doWork(); /* do some logging */ }
};

struct concrete_class : private provide_work&amp;lt;concrete_class&amp;gt;
{
    friend class provide_work&amp;lt;concrete_class&amp;gt;;
private:
    void doWork();
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;doWork()&lt;/code&gt; is an internal function without logging, so you may not want to expose it to the outside world. However, since the CRTP base class usually uses &lt;code&gt;private&lt;/code&gt; inheritance due to the nature of the composition, that cast inside &lt;code&gt;work()&lt;/code&gt; doesn’t actually work unless you make it see the inheritance through a &lt;code&gt;friend&lt;/code&gt; declaration.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!TIP]
This example works much better if you use Deducing This, in which you simply write &lt;code&gt;void work(this const auto&amp;amp;)&lt;/code&gt; and don’t worry about &lt;code&gt;friend&lt;/code&gt;s anymore.) &lt;code&gt;friend&lt;/code&gt; declarations can apply to both classes (like above) and non-member functions (like &lt;code&gt;friend void fun();&lt;/code&gt;), and in both cases, the mentioned class/function will gain access to the &lt;code&gt;private&lt;/code&gt; members of &lt;code&gt;concrete_class&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;However, in modern C++, &lt;code&gt;friend&lt;/code&gt; declarations are increasingly less necessary due to the focus on reducing coupling between classes and also strengthening encapsulation. Those relationships are usually much better expressed by utilizing a class’s &lt;code&gt;public&lt;/code&gt; API, maybe through a hidden base class. However, another (unintended?) use of &lt;code&gt;friend&lt;/code&gt; declarations has risen in popularity in recent years and has gradually become one of the most important use cases of the &lt;code&gt;friend&lt;/code&gt; keyword: the Hidden Friend Idiom.&lt;/p&gt;
&lt;p&gt;Now, what is a hidden friend? Basically, when using &lt;code&gt;friend&lt;/code&gt; to befriend a function, simply put that function’s definition right after the &lt;code&gt;friend&lt;/code&gt; declaration (define the function in-line), and you get a hidden friend.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct S
{
    S(int);
    friend void fun(S s) // hidden friend!
    {
        // implement fun(), can use private parts of S here
    }
};
S s;
fun(s); // okay
fun(2); // error!
fun(S(2)); // okay
::fun(s); // error!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A hidden friend like this is &lt;strong&gt;still&lt;/strong&gt; a non-member function, albeit residing inside the definition of a class. However, precisely because the function only has a declaration inside a class scope, it is &lt;em&gt;hidden&lt;/em&gt; against all normal lookup methods. Thus, it cannot be found from normal qualified lookup (like &lt;code&gt;::fun(s)&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;However, how is &lt;code&gt;fun(s)&lt;/code&gt; valid then? This is because hidden friends can only be found via one special rule in the lookup family: Argument-Dependent Lookup (ADL). ADL is an exception in the unqualified lookup phase that is actually invented specifically to convenience operator overloading. Basically, for ADL to happen, three conditions must be met:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The lookup performed must be an &lt;strong&gt;un&lt;/strong&gt;qualified lookup (without namespace prefix); &lt;code&gt;::fun(s)&lt;/code&gt; or &lt;code&gt;N::fun(s)&lt;/code&gt; will not invoke ADL.&lt;/li&gt;
&lt;li&gt;Normal unqualified lookup must &lt;strong&gt;only&lt;/strong&gt; find functions. This precludes the following scenario: (cannot “overload” function with non-function variables)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;namespace N { struct S {}; void fun(S); template&amp;lt;typename&amp;gt; struct Mem; }
int fun;
N::S s;
fun(s); // no ADL here, hard error
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Finally, at least one argument must be of (possibly a pointer or reference to) a class type.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When all the conditions are met, ADL specifies that a list of &lt;em&gt;associated entities&lt;/em&gt; is compiled for each (class type or pointer to or reference to a class type) argument to the function. The rules for finding associated entities are a bit complex, but in general, the following are included:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If a pointer or reference, associated entities of the referred type&lt;/li&gt;
&lt;li&gt;If a class type, then the class itself, all direct or indirect base classes, and all nested classes if the class is a nested type.&lt;/li&gt;
&lt;li&gt;In addition, if a templated class type, associated entities of all type parameters&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is not the full list of rules, but it is sufficient for this guide’s purpose. Notice especially that the second rule is not recursive: if &lt;code&gt;N::S&lt;/code&gt; derives from &lt;code&gt;M::P&lt;/code&gt;, then &lt;code&gt;M&lt;/code&gt; is not an associated namespace for &lt;code&gt;s&lt;/code&gt;. However, &lt;code&gt;M&lt;/code&gt; &lt;em&gt;is&lt;/em&gt; an associated namespace for &lt;code&gt;N::Mem&amp;lt;M::P&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;After finding all the associated entities, the associated namespace is constructed by finding the innermost enclosing namespace for each entity. Then, ADL will search all the associated namespaces, as well as all hidden friends within the associated entities. What this all means is that ADL will find two more kinds of “distant” function declarations not found by normal unqualified lookup:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;All function declarations residing in the same namespace as one of the associated entities; and&lt;/li&gt;
&lt;li&gt;All the hidden friends in associated entities&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;namespace M
{
    struct S
    {
        friend void fun(S);
    };
    void fun2(S);
}
N::S s;
fun(s); // okay, #2
fun2(s); // okay, #1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Alerted readers may ask, why is ADL a special rule invented specifically for operator overloading? Well, you see, again, operator functions are just normal functions with special names, and they can be found by ADL, too. This is especially suitable for operators because we almost never call them by the normal function syntax but instead choose to write &lt;code&gt;a + b&lt;/code&gt;, which always tries to find &lt;code&gt;operator+&lt;/code&gt; through unqualified lookup, so all the namespace-level and hidden friend &lt;code&gt;operator+&lt;/code&gt; for &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;b&lt;/code&gt; will be found. This is specifically to enable people to write operator functions inside the class’s own scope or enclosing namespace without polluting the global namespace. In fact, in the early days of C++ standardization, ADL only occurred when calling the operator through that syntactic sugar and was only extended to all functions &lt;a href=&quot;http://www.open-std.org/jtc1/sc22/wg21/docs/papers/1996/N0952.asc&quot;&gt;in 1996&lt;/a&gt;, very late in the standardization cycle.&lt;/p&gt;
&lt;p&gt;Now we know when hidden friends are found, why are they useful in terms of operator overloading? Writing operator functions as hidden friends at least have three advantages. First, hidden friends greatly &lt;em&gt;reduce&lt;/em&gt; the overload set, thus delivering significantly better compiling time and (most importantly) diagnostics. You see, if you write a normal non-member &lt;code&gt;operator+&lt;/code&gt;, it will get picked up every time everyone writes &lt;code&gt;a + b&lt;/code&gt;, no matter what type &lt;code&gt;a&lt;/code&gt; or &lt;code&gt;b&lt;/code&gt; has. Hidden friends can &lt;em&gt;only&lt;/em&gt; be found via ADL, so at least one of &lt;code&gt;a&lt;/code&gt; or &lt;code&gt;b&lt;/code&gt; must have a relevant type to your &lt;code&gt;operator+&lt;/code&gt;’s enclosing class. Otherwise, it will not be shown in the overload set (which the compiler often prints &lt;em&gt;in full&lt;/em&gt; whenever some &lt;code&gt;a + b&lt;/code&gt; goes wrong).&lt;/p&gt;
&lt;p&gt;Secondly, hidden friends &lt;strong&gt;reside physically&lt;/strong&gt; within the class scope. Yes, this is an advantage because operators are most definitely deeply tied to a class’s semantics and should form a class’s public API. If you define a non-member operator just at namespace scope, it may be separated arbitrarily from the class definition, thus making it hard to find and harder to link to the class. Also, a side note is that hidden friends also contribute to the feature-parity between non-members and member forms of operator overloading since hidden friends are still friends and can access the class’s &lt;code&gt;private&lt;/code&gt; parts. This may arguably be a good or bad thing since many operators can be implemented fully from public API, and making more friends is generally seen as weakening the encapsulation. However, since member operator functions can (obviously) already access the &lt;code&gt;private&lt;/code&gt; parts, I would argue that all operators of a class should be “seen as” members and should have equal access.&lt;/p&gt;
&lt;p&gt;Finally, one of the most important advantages of using hidden friends for operator functions, and the trick that makes it indispensable in operator overloading, is the fact that hidden friends will enable the use of &lt;a href=&quot;https://en.wikipedia.org/wiki/Barton%E2%80%93Nackman_trick&quot;&gt;the Barton-Nackman trick&lt;/a&gt;. If your class is actually templated (say, overloading &lt;code&gt;operator+&lt;/code&gt; for &lt;code&gt;Rational&amp;lt;T&amp;gt;&lt;/code&gt;), then there is a very important distinction between hidden friends and ordinary non-members:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;template&amp;lt;typename T&amp;gt;
struct Rational
{
    Rational(T); // implicit conversion from T

    // hidden friend
    friend Rational operator+(const Rational&amp;amp;, const Rational&amp;amp;);
};

// normal non-member
template&amp;lt;typename T&amp;gt;
Rational&amp;lt;T&amp;gt; operator+(const Rational&amp;lt;T&amp;gt;&amp;amp;, const Rational&amp;lt;T&amp;gt;&amp;amp;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Have you found the distinction? The hidden friend is actually &lt;strong&gt;not a template function&lt;/strong&gt;! This is a boon granted by being inside the &lt;code&gt;Rationl&amp;lt;T&amp;gt;&lt;/code&gt; class scope: you don’t need to template the operator to refer to any kind of &lt;code&gt;Rational&lt;/code&gt;; you only have to implement for the current &lt;code&gt;Rational&amp;lt;T&amp;gt;&lt;/code&gt; (can be shortened to simply &lt;code&gt;Rational&lt;/code&gt; inside the class scope, as seen above). And each invocation of &lt;code&gt;r1 + r2&lt;/code&gt; will &lt;em&gt;synthesize&lt;/em&gt; a non-template &lt;code&gt;operator+&lt;/code&gt; from &lt;code&gt;r1&lt;/code&gt; and &lt;code&gt;r2&lt;/code&gt;’s class scope.&lt;/p&gt;
&lt;p&gt;This distinction had profound implications for the usability of the operator: templated functions only do &lt;em&gt;substitution&lt;/em&gt;; they never consider any kind of &lt;em&gt;casting&lt;/em&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Rational&amp;lt;int&amp;gt; r1, r2;
r1 + r2; // okay for both form
r1 + 2; // okay for hidden friend, error (!) for non-member
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Why does &lt;code&gt;r1 + 2&lt;/code&gt; fail for a non-member declaration? Because it is a template, the compiler tries to match &lt;code&gt;2&lt;/code&gt; (aka &lt;code&gt;int&lt;/code&gt;) against &lt;code&gt;const Rational&amp;lt;T&amp;gt;&amp;amp;&lt;/code&gt; for the second argument and finds that no &lt;code&gt;T&lt;/code&gt; can satisfy this equivalence; thus, the declaration is discarded. In the hidden friend case, since &lt;code&gt;operator+&lt;/code&gt; is not a template, no substitution is needed; the compiler knows that it must try to &lt;em&gt;convert&lt;/em&gt; &lt;code&gt;2&lt;/code&gt; to some object of &lt;code&gt;Rational&amp;lt;int&amp;gt;&lt;/code&gt;, so the constructor is selected.&lt;/p&gt;
&lt;p&gt;This trick, the fact that hidden friends can strip away the template-ness of operators, is known as the Barton-Nackman trick and is the premier reason why operator overloading for templated classes is usually always done in member form or hidden friend form. (Though the most common knowledge of this trick probably stems from Item 46 of the famous &lt;em&gt;Effective C++&lt;/em&gt; book, where the same example of &lt;code&gt;Rational&amp;lt;T&amp;gt;&lt;/code&gt; is given.)&lt;/p&gt;
&lt;p&gt;However, the other two advantages remain even for non-templated classes. This is why I recommend in this guide that all operator overloading be done in member form or hidden friend form if a non-member is preferred. It leads to better compiling time, better diagnostics, better grouping, and API documentation, and enables conversion in templates. What’s there not to love?&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=I3T4lePH-yA&quot;&gt;You Must Type It Three Times&lt;/a&gt;: SFINAE Woes&lt;/h3&gt;
&lt;p&gt;Now, we venture into some more advanced topics, like the concept of SFINAE-friendly, which you should consider for each of your overloaded operators.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!IMPORTANT]
The answer to &quot;Should I make my operator SFINAE-friendly?&quot; is no 99% of the time, both because making it friendly is a bit complex and the advantage is only applicable in a very specific group of types. Most users don’t really need to care about this section. If you don’t know what SFINAE is at all, then you don’t need to read this section, as it will not really affect you.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So, what is SFINAE-friendly? This term refers to the fact that your type &lt;em&gt;perfectly forwards&lt;/em&gt; SFINAE-ness. For a friendlier example, let’s again consider the example of &lt;code&gt;Rational&amp;lt;T&amp;gt;&lt;/code&gt;. But this time, we will assume that there is a widely adopted concept &lt;code&gt;multipliable&lt;/code&gt; that tests if your type is multipliable simply by testing if &lt;code&gt;t * u&lt;/code&gt; is valid; and someone had written a function to choose different algorithm based on the multipliability of your type.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;template&amp;lt;typename T&amp;gt;
concept multipliable = requires (T t, T u) { t * u; };

template&amp;lt;multipliable T&amp;gt;
T fun(T t) { /* some specific impl */ return t * t; }

template&amp;lt;typename T&amp;gt;
T fun(T t) { /* some general impl */ return t; }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, assume that there is some wrapper on &lt;code&gt;int&lt;/code&gt;s that only allows addition, not multiplication (perhaps because there is some invariant that it must hold, and it may require too much effort to maintain in multiplication):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;template&amp;lt;typename T&amp;gt;
struct Rational
{
    T n, d;
    friend Rational operator+(const Rational&amp;amp;, const Rational&amp;amp;) { /* ... */ }
    friend Rational operator*(const Rational&amp;amp; lhs, const Rational&amp;amp; rhs)
    {
        return Rational{lhs.n * rhs.n, lhs.d * rhs.d};
    }
};

struct Number
{
    int value;
    friend Number operator+(Number, Number);
    // no operator* defined
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, on the surface, this is a very natural implementation of &lt;code&gt;operator*&lt;/code&gt;, right? It uses hidden friends as recommended (though everything below applied to regular non-members and members, too) and simply returns an object with the calculated multiplication result. However, take a look at the following result! (&lt;a href=&quot;https://godbolt.org/z/5nP85Mv7G&quot;&gt;Compiler Explorer&lt;/a&gt;)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int main()
{
    Rational&amp;lt;int&amp;gt; ri{1, 2};
    ri + ri; // good
    ri * ri; // good
    Rational&amp;lt;Number&amp;gt; rn{Number{3}, Number{4}};
    rn + rn; // good, rn * rn will obviously error out
    static_assert(multipliable&amp;lt;Rational&amp;lt;int&amp;gt;&amp;gt;); // good
    static_assert(!multipliable&amp;lt;Number&amp;gt;); // good
    static_assert(multipliable&amp;lt;Rational&amp;lt;Number&amp;gt;&amp;gt;); // ???
    fun(ri); // good, returns ri * ri
    fun(Number{3}); // good, returns Number{3} itself
    fun(rn); // hard error!
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Why is &lt;code&gt;Rational&amp;lt;Number&amp;gt;&lt;/code&gt; multipliable? And why is &lt;code&gt;fun(rn)&lt;/code&gt; a hard error?&lt;/p&gt;
&lt;p&gt;Actually, the answer to the second question directly results from the answer to the first question. It is because &lt;code&gt;multipliable&amp;lt;Rational&amp;lt;Number&amp;gt;&amp;gt;&lt;/code&gt; is satisfied, such that the specific overload for &lt;code&gt;fun&lt;/code&gt; is selected, and evaluating &lt;code&gt;t * t&lt;/code&gt; inside the body results in a hard error. So why is &lt;code&gt;multipliable&lt;/code&gt; satisfied in the first place? The answer is that &lt;code&gt;Rational&amp;lt;T&amp;gt;&lt;/code&gt;’s &lt;code&gt;operator*&lt;/code&gt; is not SFINAE-friendly.&lt;/p&gt;
&lt;p&gt;For a function to be SFINAE-friendly, it must perfectly forward the SFINAE-ness, meaning that when &lt;code&gt;lhs.n * rhs.n&lt;/code&gt; is invalid; the entire &lt;code&gt;operator*&lt;/code&gt; declaration should be SFINAE-away. However, as currently declared, &lt;code&gt;operator*&lt;/code&gt; for &lt;code&gt;Rational&amp;lt;T&amp;gt;&lt;/code&gt; is &lt;em&gt;always&lt;/em&gt; present in the overload set, and simply testing for the validness of &lt;code&gt;rn * rn&lt;/code&gt; will always succeed since you are only asking if &lt;code&gt;operator*&lt;/code&gt; exists. However, &lt;em&gt;calling&lt;/em&gt; that expression instantiated the operator and the &lt;code&gt;lhs.n * rhs.n&lt;/code&gt; line simply results in a hard error. Then how do we make it SFINAE-friendly? The solution is to forward SFINAE-ness by adding a &lt;code&gt;requires&lt;/code&gt; clause:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;template&amp;lt;typename T&amp;gt;
struct Rational
{
    // ...
    friend Rational operator*(const Rational&amp;amp; lhs, const Rational&amp;amp; rhs)
    requires requires (T t, T u) { t * u; }
    {
        return Rational{lhs.n * rhs.n, lhs.d * rhs.d};
    }
};
// Before C++20, this is typically done by
// friend auto operator*(const Rational&amp;amp; lhs, const Rational&amp;amp; rhs) -&amp;gt; decltype(lhs.n * rhs.n, void(), Rational{})
// simply doing a SFINAE test in the return type
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(Or, in this case, &lt;code&gt;requires multipliable&amp;lt;T&amp;gt;&lt;/code&gt; will do it.) Now, since &lt;code&gt;operator*&lt;/code&gt; is only present if and only if &lt;code&gt;t * u&lt;/code&gt; is valid, in the case of &lt;code&gt;Rational&amp;lt;Number&amp;gt;&lt;/code&gt;, the &lt;code&gt;operator*&lt;/code&gt; is simply &lt;em&gt;not present&lt;/em&gt; in the overload set, and &lt;code&gt;multipliable&lt;/code&gt; will now report false as &lt;code&gt;rn * rn&lt;/code&gt; is no longer valid.&lt;/p&gt;
&lt;p&gt;So, when is this technique actually useful? Actually, SFINAE-friendliness is only required in very limited cases, mostly dealing with TMP code. You only need to make your operator SFINAE-friendly if you actively &lt;em&gt;need&lt;/em&gt; &lt;code&gt;multipliable&lt;/code&gt; to detect your &lt;code&gt;operator*&lt;/code&gt; status correctly; in most cases, there is no such concept to deal with, or the user is not expecting &lt;code&gt;Rational&amp;lt;Number&amp;gt;&lt;/code&gt; to actually report its status transparently. Coupled with the fact that making functions SFINAE-friendly requires some non-trivial and non-obvious TMP work like the above &lt;code&gt;requires&lt;/code&gt; clause, it is generally not recommended to just slap those kinds of requirements on the operators. Only if you are &lt;strong&gt;sure&lt;/strong&gt; that your operator absolutely needs SFINAE-friendliness do you then do it.&lt;/p&gt;
&lt;p&gt;One example of those kinds of needs in the STL is in the context of C++20 Ranges. A very important concept for any range is the range properties, like &lt;code&gt;sized_range&amp;lt;R&amp;gt;&lt;/code&gt;. This concept basically tells you if your range is sized (i.e., can report its size in O(1) time) and is achieved by simply detecting if &lt;code&gt;ranges::size(r)&lt;/code&gt; is valid (and also provides an opt-out in the form of &lt;code&gt;disable_sized_range&amp;lt;R&amp;gt;&lt;/code&gt;). Then, each &lt;em&gt;range adaptor&lt;/em&gt; like &lt;code&gt;views::transform&lt;/code&gt; in the standard will then only provide the &lt;code&gt;size()&lt;/code&gt; member function if and only if the underlying range is sized, making &lt;code&gt;sized_range&amp;lt;transform_view&amp;lt;R, F&amp;gt;&amp;gt;&lt;/code&gt;  always equal to &lt;code&gt;sized_range&amp;lt;R&amp;gt;&lt;/code&gt;. This is a critical requirement for those adaptors to calculate/forward the range properties correctly, so &lt;code&gt;filter_view::size()&lt;/code&gt; is made SFINAE-friendly.&lt;/p&gt;
&lt;p&gt;SFINAE-friendly also has some interesting implications in the context of perfect forwarding call wrappers, which will be discussed in the section for overloading &lt;code&gt;operator()&lt;/code&gt;. Otherwise, this guide will not mention SFINAE-friendly again, and all canonical forms will assume that friendliness is not required. Please append the &lt;code&gt;requires&lt;/code&gt; clause as needed.&lt;/p&gt;
&lt;p&gt;Now that we know about some general idioms that apply to all operators, we went on to some choices that apply to some specific operators and their implications. Then, a classification of overloadable operators will be present, and the rest of the guide will focus on overloading specific operators.&lt;/p&gt;
&lt;h2&gt;Choices and Classification&lt;/h2&gt;
&lt;h3&gt;Member or Hidden Friend? A Difficult Choice&lt;/h3&gt;
&lt;p&gt;Now that we know member operator functions should either be implemented via Deducing This or (sometimes) have a ref-qualifier attached, and non-member operator functions should nearly always be implemented via Hidden Friends, the question remains: Which form should we choose? Member or non-member (hidden friends)?&lt;/p&gt;
&lt;p&gt;For some of the overloadable operators, the standard has made this choice for us:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Operators &lt;code&gt;()&lt;/code&gt;, &lt;code&gt;[]&lt;/code&gt;, &lt;code&gt;-&amp;gt;&lt;/code&gt;, &lt;code&gt;=&lt;/code&gt;, and &lt;code&gt;operator T&lt;/code&gt; (conversion) &lt;strong&gt;must&lt;/strong&gt; be overloaded via the member form.&lt;/li&gt;
&lt;li&gt;Operator &lt;code&gt;swap&lt;/code&gt; and &lt;code&gt;operator &quot;&quot;s&lt;/code&gt; (UDL) &lt;strong&gt;must&lt;/strong&gt; be overloaded via the non-member form.&lt;/li&gt;
&lt;li&gt;When (mis)used as Input/Output operators, operators &lt;code&gt;&amp;lt;&amp;lt;&lt;/code&gt; and &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt; &lt;strong&gt;must&lt;/strong&gt; be overloaded via the non-member form.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For all other scenarios, there is no restriction: you can choose freely between the member form and the non-member form. However, again, there exists a canonical form (a custom) for which operators should be members, as a non-member &lt;code&gt;operator+=&lt;/code&gt; just seems weird, while a member &lt;code&gt;operator+&lt;/code&gt; seems equally weird.&lt;/p&gt;
&lt;p&gt;In general, the rule of thumb here is that whenever the operator is unary, or binary and needs to modify its left-hand operand, it should be overloaded as a member; otherwise (binary and non-modifying), it should be a non-member. Notice here that this rule &lt;em&gt;technically&lt;/em&gt; says nothing — there is no regulation requiring your &lt;code&gt;operator+=&lt;/code&gt; to modify the left-hand operand. However, again, &lt;strong&gt;remember the motto.&lt;/strong&gt; Your operators should all be following what the builtin ones do; &lt;code&gt;+=&lt;/code&gt; on &lt;code&gt;int&lt;/code&gt;s (and other builtin types) performs the operation &lt;code&gt;a = a + b&lt;/code&gt;, and thus you should follow that convention.&lt;/p&gt;
&lt;p&gt;The operators that are customarily left-hand modifying, and thus nearly always overloaded as members include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;All the compound assignment operator &lt;code&gt;@=&lt;/code&gt;, where &lt;code&gt;@&lt;/code&gt; is one of &lt;code&gt;+ - * / % &amp;amp; | ^ &amp;lt;&amp;lt; &amp;gt;&amp;gt;&lt;/code&gt;. These are the most obvious bunch, since &lt;code&gt;operator=&lt;/code&gt; had already been required to be a member, and those are closely tied to &lt;code&gt;=&lt;/code&gt; since you should always make &lt;code&gt;a @= b&lt;/code&gt; and &lt;code&gt;a = a @ b&lt;/code&gt; equivalent.&lt;/li&gt;
&lt;li&gt;The increment and decrement operator &lt;code&gt;++&lt;/code&gt; and &lt;code&gt;--&lt;/code&gt; (both forms). These also modify their arguments, and since &lt;code&gt;++a&lt;/code&gt; is equivalent to &lt;code&gt;a += 1&lt;/code&gt; (at least I hope you make it so), they belong in the same category.&lt;/li&gt;
&lt;li&gt;All the unary operators. These include &lt;code&gt;u+&lt;/code&gt;, &lt;code&gt;u-&lt;/code&gt;, &lt;code&gt;u*&lt;/code&gt;, &lt;code&gt;u&amp;amp;&lt;/code&gt;, &lt;code&gt;!&lt;/code&gt;, &lt;code&gt;~&lt;/code&gt;, and &lt;code&gt;co_await&lt;/code&gt;. Although those do not (usually) modify their arguments, the unary-ness makes them tied closely to the argument and thus suitable for being a member.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All other operators should be overloaded as non-members. The reasoning for the existence of those rules is that left-modifying operators are naturally asymmetrical towards their two arguments and also tie more closely to the LHS since it needs to modify the argument. Therefore, they should use an asymmetrical syntax, namely overload as a member function. Other binary, non-modifying operators often treat their arguments equally (there are exceptions to this, like &lt;code&gt;-&amp;gt;*&lt;/code&gt;) and expect the same treatment (conversion, etc.) to happen to both operators, which member functions cannot provide. As for why non-modifying unary operators are recommended to members, too, that’s probably just a customary thing since non-member &lt;code&gt;operator*&lt;/code&gt; just seems too weird.&lt;/p&gt;
&lt;h3&gt;The Big Classification&lt;/h3&gt;
&lt;p&gt;Finally, after all the preludes, the general introduction stops here. The rest of the guide will be tailored to each operator, as their similarities and general principles have already been introduced, and the rest of the text will introduce each operator’s intricacies and conventions in detail. However, before we can start our journey for real, we still need to classify all the operators into several groups since each operator group still has some generality that can be introduced together (such as compound assignment operators &lt;code&gt;@=&lt;/code&gt; are practically following the same principle, even though &lt;code&gt;@&lt;/code&gt; can be different).&lt;/p&gt;
&lt;p&gt;There are many different ways to classify operators:&lt;/p&gt;
&lt;p&gt;By arity:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Unary: &lt;code&gt;u+&lt;/code&gt;, &lt;code&gt;u-&lt;/code&gt;, …&lt;/li&gt;
&lt;li&gt;Binary: &lt;code&gt;b+&lt;/code&gt;, &lt;code&gt;b-&lt;/code&gt;, …&lt;/li&gt;
&lt;li&gt;N-ary: &lt;code&gt;()&lt;/code&gt;, &lt;code&gt;[]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;N/A (Can’t talk about arity): Conversion &lt;code&gt;operator T&lt;/code&gt; (technically unary, but too different to count) and UDL &lt;code&gt;operator &quot;&quot;s&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By membership-ness:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Required to be members: &lt;code&gt;()&lt;/code&gt;, &lt;code&gt;[]&lt;/code&gt;, &lt;code&gt;-&amp;gt;&lt;/code&gt;, &lt;code&gt;=&lt;/code&gt;, &lt;code&gt;operator T&lt;/code&gt; (conversion)&lt;/li&gt;
&lt;li&gt;Usually members: &lt;code&gt;u*&lt;/code&gt;, &lt;code&gt;+=&lt;/code&gt;, …&lt;/li&gt;
&lt;li&gt;Usually non-members: &lt;code&gt;b+&lt;/code&gt;, &lt;code&gt;b-&lt;/code&gt;, …&lt;/li&gt;
&lt;li&gt;Required to be non-members: &lt;code&gt;swap&lt;/code&gt;, &lt;code&gt;&amp;lt;&amp;lt;&lt;/code&gt;, &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt; (as I/O), &lt;code&gt;operator &quot;&quot;s&lt;/code&gt; (UDL)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;However, in this guide, the operators will be classified through &lt;em&gt;how often you should overload them&lt;/em&gt; (ordered from most frequently to least):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#the-good-four&quot;&gt;The Good Four&lt;/a&gt;: &lt;code&gt;=&lt;/code&gt;, &lt;code&gt;&amp;lt;=&amp;gt;&lt;/code&gt;, &lt;code&gt;==&lt;/code&gt;, &lt;code&gt;swap&lt;/code&gt;. Those are the &lt;strong&gt;only&lt;/strong&gt; operators that you should consider overloading for all classes. All other operators below this category are only meant to be overloaded for specialized kinds of classes, not universally.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;[!IMPORTANT]
This does &lt;strong&gt;not&lt;/strong&gt; mean that you &lt;strong&gt;should&lt;/strong&gt; always overload these operators since the first rule for operator overloading is still &lt;strong&gt;Don’t Do It!&lt;/strong&gt;. Only comparatively, those are the most commonly overloaded operators.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#functors-overloading-operator&quot;&gt;The Functors&lt;/a&gt;: &lt;code&gt;()&lt;/code&gt;. The call operator is so special and common that it deserves its own group.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#simulating-a-pointer&quot;&gt;The Pointer and Iterator&lt;/a&gt;: &lt;code&gt;u*&lt;/code&gt;, &lt;code&gt;-&amp;gt;&lt;/code&gt;, &lt;code&gt;-&amp;gt;*&lt;/code&gt;, &lt;code&gt;[]&lt;/code&gt;, &lt;code&gt;++&lt;/code&gt;, and &lt;code&gt;--&lt;/code&gt; (all forms). You should consider overloading these operators only for &lt;strong&gt;pointer-like&lt;/strong&gt; or &lt;strong&gt;iterator-like&lt;/strong&gt; classes. Notice that increment and decrement appear twice; that’s because they have completely different meanings here and below.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#user-defined-literal-hidden-pearl-of-c&quot;&gt;UDLs&lt;/a&gt;: &lt;code&gt;operator &quot;&quot;s&lt;/code&gt;. This is very interesting and sufficiently different from all other operators that it deserves its own group. Definitely take a read, though; it may be more commonly useable than you think!&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#arithmetic-operators&quot;&gt;The Arithmetic&lt;/a&gt;: These are the operators you should consider overloading only for &lt;strong&gt;number-like&lt;/strong&gt; classes. Multiple subgroups exist: (still ordered by often-ness)
&lt;ul&gt;
&lt;li&gt;Normal Arithmetic: &lt;code&gt;b+&lt;/code&gt;, &lt;code&gt;b-&lt;/code&gt;, &lt;code&gt;b*&lt;/code&gt;, &lt;code&gt;/&lt;/code&gt;, &lt;code&gt;%&lt;/code&gt;. These should be considered for most number-like classes.&lt;/li&gt;
&lt;li&gt;Weirdos: &lt;code&gt;u+&lt;/code&gt; and &lt;code&gt;u-&lt;/code&gt;. Whenever &lt;code&gt;b+&lt;/code&gt; and &lt;code&gt;b-&lt;/code&gt; are overloaded, these should be, too, but they are still weird.&lt;/li&gt;
&lt;li&gt;Increment/Decrement: &lt;code&gt;++&lt;/code&gt;, &lt;code&gt;--&lt;/code&gt; (all forms). In general, most classes that are &lt;strong&gt;closed&lt;/strong&gt; on &lt;code&gt;b+&lt;/code&gt; and &lt;code&gt;b-&lt;/code&gt; should consider these.&lt;/li&gt;
&lt;li&gt;Bitwise Arithmetic: &lt;code&gt;|&lt;/code&gt;, &lt;code&gt;b&amp;amp;&lt;/code&gt;, &lt;code&gt;~&lt;/code&gt;, &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;&amp;lt;&lt;/code&gt;. These should &lt;strong&gt;only&lt;/strong&gt; be considered for number-like classes for which a bitwise interface makes sense.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#coroutine-internals-overloading-operator-co_await&quot;&gt;Coroutine&lt;/a&gt;: &lt;code&gt;co_await&lt;/code&gt;. This is also special and deserves its own group. However, you, as a user, probably never need to overload this operator.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#the-bad-nine&quot;&gt;The Bad Nine&lt;/a&gt;: &lt;code&gt;u&amp;amp;&lt;/code&gt;, &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;, &lt;code&gt;||&lt;/code&gt;, &lt;code&gt;,&lt;/code&gt;, all &lt;code&gt;new&lt;/code&gt;/&lt;code&gt;delete&lt;/code&gt; forms, and &lt;code&gt;operator T&lt;/code&gt; (conversion). &lt;strong&gt;Here lies the evil ones.&lt;/strong&gt; Under normal circumstances, you should &lt;strong&gt;never&lt;/strong&gt; overload these operators at all, no matter what kind of class you are dealing with.&lt;/li&gt;
&lt;li&gt;Irrelevant: &lt;code&gt;&amp;lt;&lt;/code&gt;, &lt;code&gt;&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;=&lt;/code&gt;, &lt;code&gt;&amp;gt;=&lt;/code&gt;, &lt;code&gt;!=&lt;/code&gt;, &lt;code&gt;!&lt;/code&gt;. These are the lowest category, however, not because they are evil or anything. It’s just that overloading those operators is completely pointless, and you should not bother with any of those since it doesn’t matter at all in functionality.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The rest of the guide will follow this classification (not in order, click the above links to jump), so please just jump to the corresponding operator group you want to learn about. Let the journey in the operator zoo finally begin!&lt;/p&gt;
&lt;h2&gt;The Good Four&lt;/h2&gt;
&lt;h3&gt;Simple Assignment: &lt;code&gt;operator=&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;TLDR&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// canonical forms
T&amp;amp; operator=(this T&amp;amp;, const T&amp;amp;); // best
T&amp;amp; operator=(const T&amp;amp;) &amp;amp;; // also okay
T&amp;amp; operator=(this T&amp;amp;, T&amp;amp;&amp;amp;) noexcept; // best
T&amp;amp; operator=(T&amp;amp;&amp;amp;) &amp;amp; noexcept; // also okay

// forms that are useful in specific circumstances
T&amp;amp; operator=(this T&amp;amp;, T) noexcept;
T&amp;amp; operator=(T) &amp;amp; noexcept;
template&amp;lt;typename U&amp;gt;
T&amp;amp; operator=(this T&amp;amp;, /* const U&amp;amp; or const T&amp;lt;U&amp;gt;&amp;amp; or ... */);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;The Basics&lt;/h4&gt;
&lt;p&gt;Ah, &lt;code&gt;=&lt;/code&gt;, the most commonly overloaded operator, and also the operator with one of the most complex stories, guidelines, and mechanisms behind it. It is so special, yet so commonplace, presented in nearly every single class that many people do not even realize its complexity. This operator is tied so deeply into value semantics, one of the core characteristics of C++, such that understanding &lt;code&gt;=&lt;/code&gt; is probably all they need to know about operator overloading for 90% of the people. In fact, &lt;code&gt;operator=&lt;/code&gt; is the &lt;em&gt;only&lt;/em&gt; operator that the compiler will automatically synthesize for you, even without you writing anything! Their importance can be seen in this special treatment.&lt;/p&gt;
&lt;p&gt;One thing to be clear here: even though &lt;code&gt;operator=&lt;/code&gt; is a binary operator and has to be overloaded in the member form, there are absolutely no restrictions on its argument type and return type; you can write &lt;code&gt;Y X::operator=(Z) const volatile &amp;amp;&amp;amp;&lt;/code&gt; and the compiler will not say anything. However, &lt;em&gt;if&lt;/em&gt; you write some specific forms of &lt;code&gt;operator=&lt;/code&gt; overloads, then the compiler will treat them specially. Those special forms are: (assuming we are overloading &lt;code&gt;operator=&lt;/code&gt; inside class &lt;code&gt;X&lt;/code&gt;)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If the argument type is &lt;code&gt;X&lt;/code&gt; or &lt;code&gt;cv X&amp;amp;&lt;/code&gt; (where &lt;em&gt;cv&lt;/em&gt; is any combination of &lt;code&gt;const&lt;/code&gt; and &lt;code&gt;volatile&lt;/code&gt;), then this overload is a &lt;strong&gt;copy assignment operator&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;If the argument type is &lt;code&gt;cv X&amp;amp;&amp;amp;&lt;/code&gt;, then this overload is a &lt;strong&gt;move assignment operator&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;[!NOTE]
There are no requirements on the return type, and a class can have more than one copy/move assignment operator since multiple forms are possible, and they can be overloaded.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The importance of these two operators is shown in their names: whenever a copy assignment happens, one copy assignment operator will be invoked; whenever a move assignment happens, one move assignment operator will be invoked. You may think this is obvious nonsense, but beware: not all use of &lt;code&gt;=&lt;/code&gt; triggers &lt;code&gt;operator=&lt;/code&gt;!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;A a;
A a2 = a; // NOT a copy assignment
a2 = a; // a copy assignment
A a3 = std::move(a); // NOT a move assignment
a3 = std::move(a); // a move assignment
const A ca;
a3 = std::move(ca); // (usually) NOT a move assignment
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The reason for this distinction is the liberal allowance of &lt;code&gt;=&lt;/code&gt;’s appearance in &lt;a href=&quot;https://en.cppreference.com/w/cpp/language/copy_initialization&quot;&gt;copy initialization&lt;/a&gt;; the syntax &lt;code&gt;T some_obj = other&lt;/code&gt; and &lt;code&gt;T some_obj = {other}&lt;/code&gt; (and array versions of these) both invoke a &lt;em&gt;constructor&lt;/em&gt;, not an &lt;code&gt;operator=&lt;/code&gt;. Similarly, &lt;code&gt;T some_obj = {a, b, c}&lt;/code&gt; is a &lt;a href=&quot;https://en.cppreference.com/w/cpp/language/list_initialization&quot;&gt;&lt;em&gt;copy-list-initialization&lt;/em&gt;&lt;/a&gt;, and also do not invoke an &lt;code&gt;operator=&lt;/code&gt;. Such distinction is unfortunate; fortunately, these are all the special rules with regards to &lt;code&gt;=&lt;/code&gt;, and all other uses of &lt;code&gt;=&lt;/code&gt; actually &lt;em&gt;do&lt;/em&gt; invoke a suitable &lt;code&gt;operator=&lt;/code&gt; following the ordinary overload resolution rules. From the above example, we can see that &lt;code&gt;operator=&lt;/code&gt; is invoked whenever you want to copy/move the contents of one object into another preexisting object, hence the name of those forms. Since a copy assignment operator, by definition, &lt;em&gt;copies&lt;/em&gt; the contents of another object into this one, it does not modify the other object and &lt;em&gt;does&lt;/em&gt; modify the &lt;code&gt;this&lt;/code&gt; object. Thus, its parameter type should be &lt;code&gt;const T&amp;amp;&lt;/code&gt;, and its object parameter type should be &lt;code&gt;T&amp;amp;&lt;/code&gt;, thus leading to the two canonical forms shown above. (See the &lt;a href=&quot;#deducing-this-a-retrospective-and-a-mistake-unfixed&quot;&gt;above sections&lt;/a&gt; for why we use &lt;code&gt;&amp;amp;&lt;/code&gt; as the &lt;em&gt;ref-qualifier&lt;/em&gt; instead of omitting it, as seen in most tutorials you may have seen before.)&lt;/p&gt;
&lt;p&gt;This topic leads to another trap regarding &lt;code&gt;operator=&lt;/code&gt;: &lt;code&gt;std::move&lt;/code&gt; does not actually move anything, which highlights how poor a name that function has. In fact, the only thing &lt;code&gt;std::move&lt;/code&gt; does is a cast to rvalue, which on the surface seems to have nothing to do with move at all! In fact, the term “move assignment operator” is, in reality, just a custom; we are assuming &lt;em&gt;rvalue means short-liveness&lt;/em&gt;, and thus an &lt;code&gt;operator=&lt;/code&gt; that only accepts rvalue operators can assume that its parameter will not be used later (either because of its lifetime is actually short, or because as a custom the caller use rvalues to signal they will not use the parameter anymore), and thus can steal the value of the parameter instead of copying it. Such an assumption often leads to much better performance compared to copying:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Example vector implementation
template&amp;lt;typename T&amp;gt;
class vector
{
private:
    T* data;
    size_t size, capacity; // actually, the most common implementation is 3 pointers

public:
    /* ... */
    vector&amp;amp; operator=(this vector&amp;amp; self, vector&amp;amp;&amp;amp; other) noexcept
    {
        /* ... destroy self&apos;s data array ... */
        self.data = other.data;
        self.size = other.size;
        self.capacity = other.capacity;
    }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, apart from the destruction of &lt;code&gt;self&lt;/code&gt;’s pointer, all the move assignment operator has to do is copy some pointer and integer values, thus achieving O(1) effectiveness, which is inherently not possible for copying.&lt;/p&gt;
&lt;p&gt;With this knowledge, we can understand why the above &lt;code&gt;a3 = std::move(ca)&lt;/code&gt; is not a move assignment operation. &lt;code&gt;std::move&lt;/code&gt; cast the right-hand expression to be of type &lt;code&gt;const A&amp;amp;&amp;amp;&lt;/code&gt;, which &lt;em&gt;is&lt;/em&gt; an rvalue but is also &lt;code&gt;const&lt;/code&gt;, which prohibits the canonical move assignment operator to be called. The reason that the canonical version used non-&lt;code&gt;const&lt;/code&gt; rvalue references is that it needs to steal the value from &lt;code&gt;other&lt;/code&gt;, thus needing to modify it. In such cases, the call will be silently degrading to a copy assignment by the overload resolution rules (&lt;code&gt;const A&amp;amp;&amp;amp;&lt;/code&gt; cannot be bound to &lt;code&gt;A&amp;amp;&amp;amp;&lt;/code&gt;, but can be bound to &lt;code&gt;const A&amp;amp;&lt;/code&gt;, as &lt;code&gt;const&lt;/code&gt;-ness cannot be silently stripped), thus often resulting in worse performance. (This is also a good argument &lt;em&gt;against&lt;/em&gt; &lt;code&gt;const&lt;/code&gt; all the things.)&lt;/p&gt;
&lt;p&gt;However, astute readers will throw a question at me after reading the above paragraph: using rvalues to mean short-liveness is just a &lt;em&gt;custom&lt;/em&gt;, not a &lt;em&gt;rule&lt;/em&gt;! The language has no enforcement on this custom, which means that the signature of the move assignment is a lie! Indeed, &lt;em&gt;value category is not lifetime&lt;/em&gt;. It is true that temporary values are often rvalues, but often is not always, and the reverse is also not true:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;void fun(std::string s)
{
    std::string s1 = &quot;Hello&quot;, s2;
    s2 = std::move(s1); // (1) Actually long-lived rvalues
    std::println(&quot;{}&quot;, s1); // OOPS, read from moved-from objects

    s2 = s; // (2) Actually short-lived lvalues
    // OOPS, a copy, even though s will be discarded after the next line anyway
    std::println(&quot;{}&quot;, s2);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The (1) case is unfortunate in that an rvalue actually referenced a long-lived object (&lt;code&gt;s1&lt;/code&gt;) that is used after the move assignment, resulting in reading from a moved-from object that gets an unspecified (but valid) value. Such a read is potentially dangerous if the logic after still expects &lt;code&gt;s1&lt;/code&gt; to retain its original value. The (2) case is also unfortunate, in that even though &lt;code&gt;s&lt;/code&gt; is not used after the assignment and will end its lifetime very soon, since it is a lvalue, the assignment must perform a copy, even though a move would suffice. Both cases exposed that rvalue actually has nothing to do with short-liveness. Thus, the signature of the move assignment will indeed cause inconvenience in some cases.&lt;/p&gt;
&lt;p&gt;A full solution to those two cases requires connecting lifetime with value categories more firmly in C++. For example, for (1), the solution will be to introduce destructive moves, where moved-from objects cannot be used at all, thus preventing the danger. For (2), the solution would be to treat the definite last use of variables as rvalues automatically, thus eliminating this potential inefficiency. In fact, the language is already slowly moving in this direction in a small but crucial case: the &lt;code&gt;return&lt;/code&gt; statement.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;std::string fun()
{
    std::string s;
    return s; // If no NRVO occurs, then a guaranteed move construction here; no copy!
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Even though &lt;code&gt;s&lt;/code&gt; is definitely a lvalue here, the language requires that such a direct &lt;code&gt;return&lt;/code&gt; statement for local variables &lt;em&gt;must&lt;/em&gt; treat its argument as an rvalue &lt;a href=&quot;https://wg21.link/P2266&quot;&gt;since C++23&lt;/a&gt;, since this is &lt;em&gt;definitely&lt;/em&gt; the last use of the local variable. This is called “implicit move” and has been an optimization well-known to the compilers since C++11. However, such treatment has not expanded to other definite last uses (yet?), and even if it does, cases where the variable is not the last use but its value is not needed anymore (such as dead stores) will still not be optimized, demonstrating the weakness in the rvalue abstraction.&lt;/p&gt;
&lt;p&gt;The (1) case is more difficult and dangerous since it caused unexpected behavior instead of just a performance reduction. For this reason, the language introduced &lt;code&gt;std::move&lt;/code&gt; to explicitly signal the creation of rvalues, and to inform the writer that the variable’s value better not be used after this statement. This custom is not enforced, nor is it perfect, but that’s what we are now, and the status quo is unlikely to change anymore. Just beware of this weak equality between rvalue and short-liveness and move on with life, then.&lt;/p&gt;
&lt;h4&gt;Automatic Generation Rules: The Rule of Three, The Rule of Five, and The Rule of Zero&lt;/h4&gt;
&lt;p&gt;Back on topic. I said earlier that &lt;code&gt;operator=&lt;/code&gt; is the only operator that will be generated by the compiler even if you don’t write anything. A natural question arises: when will the compiler generate them, and what does the generated version do?&lt;/p&gt;
&lt;p&gt;The second question is easier to answer:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If the class in question is a normal class, the generated version of &lt;code&gt;operator=&lt;/code&gt; will do the same operation &lt;em&gt;memberwise&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;If the class in question is a union, the generated version of &lt;code&gt;operator=&lt;/code&gt; will do a copy of the object representation (in other words, copy all the bytes of the object, as if by &lt;code&gt;std::memmove&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are two nuances worth pointing out with this seemingly simple description. First, the definition of &lt;em&gt;member&lt;/em&gt; is not just &lt;em&gt;data&lt;/em&gt; members but &lt;em&gt;subobjects&lt;/em&gt;. The difference between those two terms is that the latter also includes the direct bases of a class since, in the C++ object representation, the base subobjects come first before any data members. (Astute readers may ask about what will happen for virtual indirect bases that are inherited multiple times, as they are guaranteed to only appear once in the object representation, but their construction requires coordination from even indirect subclasses. The answer is simple: whether their subobjects are assigned one time or multiple times in the implementation of the implicitly-defined assignment operators is unspecified. &lt;em&gt;Sigh&lt;/em&gt;) The term “subobjects” also refers to each element of an array member and not the array itself, which guarantees the correct generation of default copy/move assignment for array members since built-in array types do not have an assignment operator at all.&lt;/p&gt;
&lt;p&gt;Another nuance with regards to unions is what is not said in the second bulletin: The &lt;em&gt;only&lt;/em&gt; thing the automatically generated &lt;code&gt;operator=&lt;/code&gt;s for a union will do is copy the bytes; notably, no calls to the data members’ &lt;code&gt;operator=&lt;/code&gt; will happen! This is obviously a problem since non-trivial data members are non-trivial &lt;em&gt;because&lt;/em&gt; their &lt;code&gt;operator=&lt;/code&gt; does different things than just copying the bytes. For instance, &lt;code&gt;std::vector&lt;/code&gt;’s copy assignment operator needs to allocate a new buffer in case the current buffer is not large enough, copy over the elements, and adjust the size/capacity pointer/member. If only the bytes are copied over, two &lt;code&gt;std::vector&lt;/code&gt;s will refer to the same memory, which will result in a guaranteed double-deletion. It is for this reason that we say C++ unions are unsafe, and you need some auxiliary structure to keep track of the active member and overload the &lt;code&gt;operator=&lt;/code&gt;s to call the appropriate underlying &lt;code&gt;operator=&lt;/code&gt; is a necessity unless you only have trivial members. (Or, better yet, use a safe wrapper that handles these chores for you, such as &lt;code&gt;std::variant&lt;/code&gt;.)&lt;/p&gt;
&lt;p&gt;The questions of &lt;em&gt;when&lt;/em&gt; will the compiler generate &lt;code&gt;operator=&lt;/code&gt;s for you are a lot more complicated to answer. Obviously, if you are not writing a copy/move assignment operator, the compiler will generate one for you, right? Wrong! The rule for when the copy and move assignment operators are generated are encoded in the so-called Rule of Three and Rule of Five, where the former applies to C++98/03, and the latter applies to C++11 and later:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Rule of Three&lt;/strong&gt;: If you declare any of a copy constructor, copy assignment operator, or destructor, you should declare all three&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rule of Five&lt;/strong&gt;: If you declare any of a copy constructor, move constructor, copy assignment operator, move assignment operator, or destructor, you should declare all five&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now, the rules aren’t that simple: Declaring a copy constructor won’t affect the generation of a copy assignment operator (and vice versa), and declaring a destructor won’t affect the generation of a copy constructor or a copy assignment operator. However, for all other relationships (basically, those interacting with move operations, which C++11 can fix right away), the Rule of Five &lt;em&gt;does&lt;/em&gt; apply, and the above irregularities are actually deprecated behavior. Therefore, personally, I recommend just treating the rule as-if by Rule of Five: if you declare any one of the five special member functions, &lt;strong&gt;all&lt;/strong&gt; five will not be automatically generated. It is not the truth, but close enough to be a guideline to follow.&lt;/p&gt;
&lt;p&gt;The reasoning behind Rule of Five and C++11’s forceful enforcement of it is due to an acronym commonly thrown around by C++ enthusiasts: RAII or Resource Acquisition Is Initialization. Now, this acronym is actually wrong; the behavior/principle that people actually &lt;em&gt;mean&lt;/em&gt; when they say RAII is &lt;em&gt;Resource Release Is Destruction&lt;/em&gt;, but RRID is not a good acronym, so we came up with RAII. The driving principle behind this idea is to treat C++ destructors as resource releasers:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct LockGuard
{
    std::mutex m;
    ~LockGuard() { m.unlock(); }
};

{
    std::mutex m; m.lock();
    LockGuard lk{m};
    /* ... no matter what happens here, even under an exception, the mutex is always unlocked */
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Such resource management classes (or “RAII classes”) are the cornerstone of modern C++ resource management, and countless examples of these kinds of classes have found their way into the standard (&lt;code&gt;std::lock_guard&lt;/code&gt;, smart pointer, &lt;code&gt;std::jthread&lt;/code&gt;, IOStreams, …) and third-party libraries. They serve the same function as &lt;code&gt;finally&lt;/code&gt; clauses in other languages serve: to ensure resource release always happens, no matter which exit path the code takes.&lt;/p&gt;
&lt;p&gt;One crucial question to be answered for RAII classes is how their copying behavior is. A lot of choices exist, and each of them has its own benefits, drawbacks, and use cases such that no one is the preferred approach. For instance, you can do:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Unique Ownership&lt;/strong&gt;: The easiest way out. Just forbid copying and only allow moving (in some rare cases, you can even disallow moving if there is no suitable empty state). For memory resources, this is &lt;code&gt;std::unique_ptr&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deep-Copying&lt;/strong&gt;: If the underlying resource is not unique, just copy the resource on every copy of the management class. For memory resources, this is &lt;code&gt;std::indirect&lt;/code&gt; or &lt;code&gt;std::polymorphic&lt;/code&gt; (&lt;a href=&quot;https://wg21.link/P3019&quot;&gt;C++26&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reference Counting&lt;/strong&gt;: One of the more complex approaches. Keep a carefully protected shared counter of the number of copies for each resource, and only release if the copy goes down to zero. This often requires intrusive bookkeeping or heap allocation while also requiring careful protection of concurrent modifications to the counter (through atomics or locks), and thus is much more heavyweight and more flexible than the above approaches. For memory resources, this is &lt;code&gt;std::shared_ptr&lt;/code&gt; or &lt;code&gt;boost::intrusive_ptr&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Shared Ownership&lt;/strong&gt;: If we can do a safe shared counter, why not share the entire resource? This requires coordination from the resource itself, such as the presence of concurrent queues or locks to safely handle concurrent requests and also some way of handling multiple releases. This essentially combines management classes into the resource itself. The management class can be a simple observer/view that has trivial copying.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All of these require different &lt;code&gt;operator=&lt;/code&gt; behavior. For unique ownership, normally you want to not generate &lt;code&gt;operator=&lt;/code&gt;; for the rest, you want the generation of &lt;code&gt;operator=&lt;/code&gt; but with vastly different behavior. For instance, shared ownership management classes can do with the default memberwise behavior, but for reference counting, that would be a disaster. It is precisely because of the lack of a preferred approach that Rule of Five exists: If you are writing a destructor, you are probably writing a RAII class, and in that case, you should write out the behavior of copy/move operations directly and explicitly; automatic generation is usually wrong. Reversely, if you are customizing copy/move operations, you are probably managing some kind of resources, and you should write a destructor to be an RAII class.&lt;/p&gt;
&lt;p&gt;Rule of Five is great and does prevent a lot of mistakes; however, writing classes under this rule is really annoying:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Widget
{
private:
    SomeResource resource;

public:
    // I&apos;m managing resources; let&apos;s make this a RAII class
    ~Widget() { resource.release(); }

    // OOPS, that disable move operations; I want the shared ownership behavior, where defaulted copy/move suffice
    Widget(Widget&amp;amp;&amp;amp;) noexcept = default;
    Widget&amp;amp; operator=(Widget&amp;amp;&amp;amp;) &amp;amp; noexcept = default;

    // OOPS, those now disable copy operations
    Widget(const Widget&amp;amp;) = default;
    Widget&amp;amp; operator=(const Widget&amp;amp;) &amp;amp; = default;
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Although it is correct to force you to always write out the intention explicitly, people are still lazy. In that case, I have a better rule to follow when overloading &lt;code&gt;operator=&lt;/code&gt; as a special member function:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Rule of Zero&lt;/strong&gt;: If you declare any of a copy constructor, move constructor, copy assignment operator, move assignment operator, or destructor, you should declare all five; &lt;strong&gt;however, normal classes shouldn’t define any of them; leave these to a (preferably standard) class specifically dealing with ownships&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;class Widget
{
private:
    std::unique_ptr&amp;lt;SomeResource, decltype([](auto&amp;amp; r) { r.release(); })&amp;gt; resource;

public:
    // No need for a destructor!
    // Therefore, no need to manually restore move!
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once you factor out the handling of ownership into its own class, you’ll suddenly find that the automatically generated version Just Works. What a relief! Even better, as listed above, many common ownership handling classes have a standard version that handles everything for you, so ideally, you don’t ever need to write those five special members at all! This is why it is called the Rule of &lt;em&gt;Zero&lt;/em&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!NOTE]
Even though things like &lt;code&gt;unique_ptr&lt;/code&gt; handles memory resources, they all supported some form of custom deleters that allows you to handle arbitrary release behaviors; of course, it’s better to use more specific classes that have a better interface, such as preferring &lt;code&gt;std::fstream&lt;/code&gt; over &lt;code&gt;std::unique_ptr&amp;lt;FILE&amp;gt;&lt;/code&gt;)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;None of us lives in an ideal world, but at least you should factor out your ownership logic into its own (preferably generic) class and enjoy Rule of Zero for the rest.&lt;/p&gt;
&lt;h4&gt;When Compiler Fails: Defaulted as deleted, and why it matters&lt;/h4&gt;
&lt;h4&gt;Copy-and-Swap Idiom: When and How&lt;/h4&gt;
&lt;h4&gt;More Idioms&lt;/h4&gt;
&lt;h5&gt;Copy-and-Move Idiom&lt;/h5&gt;
&lt;h5&gt;Implementing Constructors by Assignment&lt;/h5&gt;
&lt;h5&gt;A Nightmare Operator: Deal with &lt;code&gt;optional&amp;lt;T&amp;amp;&amp;gt;&lt;/code&gt;&lt;/h5&gt;
&lt;h4&gt;&lt;code&gt;operator=&lt;/code&gt; That Is Not Copy/Move Assignment: Irrelevant or Optimization?&lt;/h4&gt;
&lt;h4&gt;Templated &lt;code&gt;operator=&lt;/code&gt;&lt;/h4&gt;
&lt;h4&gt;Virtual &lt;code&gt;operator=&lt;/code&gt;: Genius or Trap?&lt;/h4&gt;
&lt;h4&gt;&lt;code&gt;const operator=&lt;/code&gt;: When Is A Contradiction Useful?&lt;/h4&gt;
&lt;h3&gt;&lt;code&gt;swap&lt;/code&gt;: An Operator Disguised&lt;/h3&gt;
&lt;h4&gt;The Basics: Importance of A &lt;code&gt;noexcept swap&lt;/code&gt;&lt;/h4&gt;
&lt;h4&gt;ADL &lt;code&gt;swap&lt;/code&gt; and &lt;code&gt;ranges::swap&lt;/code&gt;: Incomplete Solution&lt;/h4&gt;
&lt;h4&gt;Member or Non-Member or Hidden Friend? A War Story&lt;/h4&gt;
&lt;h3&gt;Comparison Crash Course: &lt;code&gt;operator&amp;lt;=&amp;gt;&lt;/code&gt; and &lt;code&gt;operator==&lt;/code&gt; (and other five)&lt;/h3&gt;
&lt;h4&gt;The Basics: Primary and Secondary Comparison&lt;/h4&gt;
&lt;h4&gt;Comparison Result Types and Functions&lt;/h4&gt;
&lt;h4&gt;Rewritten Candidates and Reverse Rewrite&lt;/h4&gt;
&lt;h4&gt;The Default Situation and The Great Separation&lt;/h4&gt;
&lt;h4&gt;Spaceship Idioms: Ignoring, Reversing&lt;/h4&gt;
&lt;h2&gt;Functors: Overloading &lt;code&gt;operator()&lt;/code&gt;&lt;/h2&gt;
&lt;h3&gt;&lt;code&gt;static&lt;/code&gt;, &lt;code&gt;const&lt;/code&gt;, Lambda, &lt;code&gt;mutable&lt;/code&gt;, Oh My!&lt;/h3&gt;
&lt;h3&gt;Stateful and Pure Functors with Standard Algorithms&lt;/h3&gt;
&lt;h3&gt;Perfect-Forwarding Functors: &lt;code&gt;= delete&lt;/code&gt; and Deducing This&lt;/h3&gt;
&lt;h2&gt;Arithmetic Operators&lt;/h2&gt;
&lt;h3&gt;Compound Assignment: &lt;code&gt;operator@=&lt;/code&gt;&lt;/h3&gt;
&lt;h3&gt;Simple Arithmetic: &lt;code&gt;b+ b- b* / %&lt;/code&gt;&lt;/h3&gt;
&lt;h4&gt;The Basics&lt;/h4&gt;
&lt;h4&gt;By-Value or Symmetry: Pick Your Poison&lt;/h4&gt;
&lt;h3&gt;Bitwise Arithmetic: &lt;code&gt;| b&amp;amp; ~ &amp;gt;&amp;gt; &amp;lt;&amp;lt;&lt;/code&gt;&lt;/h3&gt;
&lt;h3&gt;Increment/Decrement: A Dilemma&lt;/h3&gt;
&lt;h4&gt;Prefix: &lt;code&gt;operator++()&lt;/code&gt; and &lt;code&gt;operator--()&lt;/code&gt;&lt;/h4&gt;
&lt;h4&gt;Postfix: &lt;code&gt;operator++(int)&lt;/code&gt; and &lt;code&gt;operator--(int)&lt;/code&gt;&lt;/h4&gt;
&lt;h3&gt;The Weirdo: Unary &lt;code&gt;operator+()&lt;/code&gt; and &lt;code&gt;operator-()&lt;/code&gt;&lt;/h3&gt;
&lt;h2&gt;Input/Output Operator&lt;/h2&gt;
&lt;h3&gt;&lt;code&gt;operator&amp;gt;&amp;gt;&lt;/code&gt; as Stream Extractor&lt;/h3&gt;
&lt;h4&gt;The Basics&lt;/h4&gt;
&lt;h4&gt;Dealing with Failure&lt;/h4&gt;
&lt;h3&gt;&lt;code&gt;operator&amp;lt;&amp;lt;&lt;/code&gt; as Stream Inserter&lt;/h3&gt;
&lt;h4&gt;The Basics&lt;/h4&gt;
&lt;h4&gt;Formatting Nightmare&lt;/h4&gt;
&lt;h3&gt;Migrate to &lt;code&gt;std::formatter&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/h3&gt;
&lt;h2&gt;Simulating a Pointer&lt;/h2&gt;
&lt;h3&gt;One-and-a-Fake Unary: &lt;code&gt;operator*()&lt;/code&gt; and &lt;code&gt;operator-&amp;gt;()&lt;/code&gt;&lt;/h3&gt;
&lt;h4&gt;The Basics: Core Pointer&lt;/h4&gt;
&lt;h4&gt;Shallow or Deep &lt;code&gt;const&lt;/code&gt;?&lt;/h4&gt;
&lt;h4&gt;The Unorthodox Arrow&lt;/h4&gt;
&lt;h3&gt;The Forgotten Sister: &lt;code&gt;operator-&amp;gt;*&lt;/code&gt;&lt;/h3&gt;
&lt;h3&gt;A Changed Friend: &lt;code&gt;operator[]&lt;/code&gt;&lt;/h3&gt;
&lt;h4&gt;The Basics: &lt;code&gt;const&lt;/code&gt;-Coercing Subscript and Deducing This&lt;/h4&gt;
&lt;h4&gt;Deploying an Multidimensional &lt;code&gt;operator[]&lt;/code&gt;&lt;/h4&gt;
&lt;h2&gt;Coroutine Internals: Overloading &lt;code&gt;operator co_await&lt;/code&gt;&lt;/h2&gt;
&lt;h3&gt;Understanding Awaiter and Awaitable&lt;/h3&gt;
&lt;h3&gt;Decoding a Coroutine&lt;/h3&gt;
&lt;h3&gt;Implementing &lt;code&gt;std::lazy&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/h3&gt;
&lt;h2&gt;The Bad Nine&lt;/h2&gt;
&lt;h3&gt;Defending Against the Dark Unary &lt;code&gt;operator&amp;amp;&lt;/code&gt;&lt;/h3&gt;
&lt;h3&gt;&lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; and &lt;code&gt;||&lt;/code&gt;: Before and After C++17&lt;/h3&gt;
&lt;h3&gt;Overloading &lt;code&gt;new&lt;/code&gt; and &lt;code&gt;delete&lt;/code&gt;: Explained&lt;/h3&gt;
&lt;h4&gt;Why, When, and How&lt;/h4&gt;
&lt;h4&gt;Global or Class-Scope?&lt;/h4&gt;
&lt;h4&gt;Placement &lt;code&gt;new&lt;/code&gt; and &lt;code&gt;nothrow new&lt;/code&gt;&lt;/h4&gt;
&lt;h4&gt;New Handler and the Memory Loop&lt;/h4&gt;
&lt;h4&gt;Placement &lt;code&gt;delete&lt;/code&gt;: The Weirdest Operator in the Standard&lt;/h4&gt;
&lt;h4&gt;Overloading &lt;code&gt;new[]&lt;/code&gt; and &lt;code&gt;delete[]&lt;/code&gt;&lt;/h4&gt;
&lt;h3&gt;A Wild Comma Ride&lt;/h3&gt;
&lt;h3&gt;Conversion Operators: The Good, The Bad, and The Irrelevant&lt;/h3&gt;
&lt;h4&gt;The One Good Conversion: &lt;code&gt;explicit operator bool()&lt;/code&gt;&lt;/h4&gt;
&lt;h4&gt;Other Niche Cases&lt;/h4&gt;
&lt;h4&gt;An Irrelevant &lt;code&gt;!&lt;/code&gt;&lt;/h4&gt;
&lt;h2&gt;User-Defined Literal: Hidden Pearl of C++&lt;/h2&gt;
&lt;h3&gt;The Basics: UDL Classification and Overloading&lt;/h3&gt;
&lt;h3&gt;Integral UDL&lt;/h3&gt;
&lt;h3&gt;Floating-Point UDL&lt;/h3&gt;
&lt;h3&gt;Character UDL&lt;/h3&gt;
&lt;h3&gt;String UDL&lt;/h3&gt;
&lt;h3&gt;Templated UDL and the &lt;code&gt;constexpr std::string&lt;/code&gt; Dilemma&lt;/h3&gt;
&lt;h2&gt;Operator Overloading in the STL: A Glimpse&lt;/h2&gt;
&lt;h3&gt;The Great Comparison Revolution&lt;/h3&gt;
&lt;h3&gt;Iterator Special: Cornerstone of STL Algorithms&lt;/h3&gt;
&lt;h4&gt;The Basics: Evolution of Iterator Category in the STL&lt;/h4&gt;
&lt;h4&gt;&lt;code&gt;u*&lt;/code&gt;, &lt;code&gt;-&amp;gt;&lt;/code&gt; and &lt;code&gt;++&lt;/code&gt;: Disguised Core&lt;/h4&gt;
&lt;h4&gt;&lt;code&gt;--&lt;/code&gt;, &lt;code&gt;+=&lt;/code&gt;, &lt;code&gt;-=&lt;/code&gt;, and &lt;code&gt;[]&lt;/code&gt;: Extending the Basic Iterator&lt;/h4&gt;
&lt;h4&gt;Comparison For Iterators: A War Story&lt;/h4&gt;
&lt;h4&gt;Sentinel: Revolution on the Old STL&lt;/h4&gt;
&lt;h4&gt;Postfix &lt;code&gt;++ --&lt;/code&gt; in C++20: A Rebellion&lt;/h4&gt;
&lt;h4&gt;&lt;code&gt;explicit operator bool&lt;/code&gt; for Ranges: A Twist&lt;/h4&gt;
&lt;h3&gt;&lt;code&gt;u*&lt;/code&gt;: Nullable, Pointer or Optional?&lt;/h3&gt;
&lt;h3&gt;Deprecating and Decreasing &lt;code&gt;operator-&amp;gt;&lt;/code&gt;&lt;/h3&gt;
&lt;h3&gt;&lt;code&gt;operator+&lt;/code&gt; For &lt;code&gt;std::string&lt;/code&gt;: A Mistake? Nightmare with &lt;code&gt;string_view&lt;/code&gt;?&lt;/h3&gt;
&lt;h3&gt;Standard Functors: Mistakes We Cannot Fix&lt;/h3&gt;
&lt;h4&gt;Three Generations of &lt;code&gt;std::less&lt;/code&gt;&lt;/h4&gt;
&lt;h4&gt;Three Generations of &lt;code&gt;std::function&lt;/code&gt;&lt;/h4&gt;
&lt;h4&gt;The Power of The &lt;code&gt;std::bind&lt;/code&gt; Family&lt;/h4&gt;
&lt;h3&gt;A Survey of Bad Operators&lt;/h3&gt;
&lt;h4&gt;Implicit Comparisons&lt;/h4&gt;
&lt;h4&gt;&lt;code&gt;operator&amp;amp;&amp;amp;&lt;/code&gt; and &lt;code&gt;operator||&lt;/code&gt;&lt;/h4&gt;
&lt;h3&gt;UDL in the STL&lt;/h3&gt;
&lt;h2&gt;The Future of Operator Overloading&lt;/h2&gt;
&lt;h3&gt;&lt;code&gt;operator?:&lt;/code&gt;: A Cure for SIMD?&lt;/h3&gt;
&lt;h3&gt;The Great Search For Dot&lt;/h3&gt;
&lt;h3&gt;Overloadable &lt;code&gt;operator^^&lt;/code&gt;: Customising Reflection&lt;/h3&gt;
&lt;h3&gt;A Pipeline-Rewrite Operator: &lt;code&gt;|&amp;gt;&lt;/code&gt;&lt;/h3&gt;
&lt;h3&gt;A Control-Flow Operator: &lt;code&gt;??&lt;/code&gt;&lt;/h3&gt;
&lt;h3&gt;A Implication Arrow: A Herculean Task&lt;/h3&gt;
&lt;h3&gt;A War Story on Pattern Matching and Operators (&lt;code&gt;is&lt;/code&gt;, &lt;code&gt;as&lt;/code&gt;, &lt;code&gt;match&lt;/code&gt;, ...)&lt;/h3&gt;
&lt;h4&gt;Basics: What Is PM And Why Should You Care?&lt;/h4&gt;
&lt;h4&gt;Chaining vs Composition: The PM Debate in C++26&lt;/h4&gt;
&lt;h4&gt;Generalized &lt;code&gt;match&lt;/code&gt; and &lt;code&gt;is&lt;/code&gt;: Converging Solutions&lt;/h4&gt;
&lt;h3&gt;Prefix UDL: String Interpolation and More&lt;/h3&gt;
&lt;h3&gt;Future of Operator Rewriting: Shooting An Arrow At The Star&lt;/h3&gt;
&lt;h4&gt;MORE Rewriting! What&apos;s Not To Like?&lt;/h4&gt;
&lt;h4&gt;Rewriting &lt;code&gt;-&amp;gt;&lt;/code&gt; and &lt;code&gt;-&amp;gt;*&lt;/code&gt;: War Signal From Library&lt;/h4&gt;
&lt;h4&gt;Rewriting Arithmetic: How Far?&lt;/h4&gt;
&lt;h3&gt;Chained Comparison: A Dream Revisited&lt;/h3&gt;
</content:encoded></item><item><title>Range Properties of the Standard Range Adaptors</title><link>https://mick235711.github.io/2022/09/20/std-range-adaptors/</link><guid isPermaLink="true">https://mick235711.github.io/2022/09/20/std-range-adaptors/</guid><description>Reference notes on the range properties of standard C++ range adaptors.</description><pubDate>Tue, 20 Sep 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Enumerate the basic usages and properties of C++20-26 Range Adaptors.&lt;/p&gt;
&lt;p&gt;In this post, &quot;range adaptors&quot; refer to both range factories (algorithm that produce range, can only be the starting point of a pipeline, like &lt;code&gt;views::single&lt;/code&gt;)
and (real) range adaptors (algorithm that takes a range and return an adapted range, like &lt;code&gt;views::filter&lt;/code&gt;).
In C++20 standard, following the adoption of Ranges TS, the standard adopted 18 range adaptors:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;5 factories: &lt;code&gt;empty&lt;/code&gt;, &lt;code&gt;single&lt;/code&gt;, &lt;code&gt;iota&lt;/code&gt;, &lt;code&gt;istream&lt;/code&gt;, &lt;code&gt;counted&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;13 (real) adaptors: &lt;code&gt;all&lt;/code&gt;, &lt;code&gt;filter&lt;/code&gt;, &lt;code&gt;transform&lt;/code&gt;, &lt;code&gt;take&lt;/code&gt;, &lt;code&gt;take_while&lt;/code&gt;, &lt;code&gt;drop&lt;/code&gt;, &lt;code&gt;drop_while&lt;/code&gt;, &lt;code&gt;join&lt;/code&gt;, &lt;code&gt;lazy_split&lt;/code&gt;, &lt;code&gt;split&lt;/code&gt;, &lt;code&gt;common&lt;/code&gt;, &lt;code&gt;reverse&lt;/code&gt;, &lt;code&gt;elements&lt;/code&gt; (&lt;code&gt;keys&lt;/code&gt;/&lt;code&gt;values&lt;/code&gt; are aliases)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Of course, this is only a small subset of what is provided in range-v3 (over 100 adaptors). C++23 greatly expanded range support in multiple ways, including the addition of 14 more adaptors:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;4 new factories: &lt;code&gt;zip&lt;/code&gt;, &lt;code&gt;zip_transform&lt;/code&gt;, &lt;code&gt;cartesian_product&lt;/code&gt;, &lt;code&gt;repeat&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;10 new (real) adaptors: &lt;code&gt;as_rvalue&lt;/code&gt;, &lt;code&gt;join_with&lt;/code&gt;, &lt;code&gt;as_const&lt;/code&gt;, &lt;code&gt;enumerate&lt;/code&gt;, &lt;code&gt;adjacent&lt;/code&gt;, &lt;code&gt;adjacent_transform&lt;/code&gt; (&lt;code&gt;pairwise&lt;/code&gt; are aliases), &lt;code&gt;chunk&lt;/code&gt;, &lt;code&gt;slide&lt;/code&gt;, &lt;code&gt;chunk_by&lt;/code&gt;, &lt;code&gt;stride&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;and C++26 is expected to provide even more (&lt;code&gt;concat&lt;/code&gt; and &lt;code&gt;maybe&lt;/code&gt; being the most expected ones).&lt;/p&gt;
&lt;p&gt;Each adaptor has its own use case, feature, and limitations. Especially, each adaptors has its own accepted range properties, and the output range&apos;s properties also differ.
These properties limitations are often not documented, in standard or elsewhere, making determine those properties a pain.
Therefore, this post serves as an expansion upon &lt;a href=&quot;https://brevzin.github.io/c++/2021/02/28/ranges-reference/&quot;&gt;the excellent post by Barry Revzin&lt;/a&gt;, adding more range adaptors and
adding more properties so that the reference is more complete.&lt;/p&gt;
&lt;p&gt;This post still follows the same convention set in the above linked post (&lt;code&gt;W w&lt;/code&gt; meaning type and value, &lt;code&gt;[T]&lt;/code&gt; means range with refernece type &lt;code&gt;T&lt;/code&gt;, &lt;code&gt;(A, B)&lt;/code&gt; means &lt;code&gt;tuple&amp;lt;A, B&amp;gt;&lt;/code&gt;, &lt;code&gt;A -&amp;gt; B&lt;/code&gt; means function taking &lt;code&gt;A&lt;/code&gt; and returning &lt;code&gt;B&lt;/code&gt;),
and the properties surveyed are the original ones (reference, category, common, sized, const-iterable, borrowed) plus an additional one:
constant (also, value type is included for convenience). A range being a constant range simply means that its iterator are constant iterator, i.e. we cannot modify its elements using its iterators,
so &lt;code&gt;const vector&amp;lt;int&amp;gt;&lt;/code&gt; is a constant range but &lt;code&gt;vector&amp;lt;int&amp;gt;&lt;/code&gt; is not. Notice that all of those 7 categories can be detected by concepts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;reference: &lt;code&gt;ranges::range_reference_t&amp;lt;R&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;value type: &lt;code&gt;ranges::range_value_t&amp;lt;R&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;category: &lt;code&gt;ranges::input_range&amp;lt;R&amp;gt;&lt;/code&gt; to &lt;code&gt;ranges::contiguous_range&amp;lt;R&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;common: &lt;code&gt;ranges::common_range&amp;lt;R&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;sized: &lt;code&gt;ranges::sized_range&amp;lt;R&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;const-iterable: &lt;code&gt;ranges::range&amp;lt;const R&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;borrowed: &lt;code&gt;ranges::borrowed_range&amp;lt;R&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;constant: &lt;code&gt;ranges::constant_range&amp;lt;R&amp;gt;&lt;/code&gt; (C++23)&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;[!IMPORTANT]
In the C++20 Ranges world, value type and reference type are two entirely different beast. Reference type is the type returned by &lt;code&gt;operator*&lt;/code&gt;,
and also the type that you interact with more commonly (ranges in this post is referred to as &lt;code&gt;[T]&lt;/code&gt;, where &lt;code&gt;T&lt;/code&gt; is its reference type),
basically you can think reference type as the element type (it is not necessarily a language reference). Value type is often a cvr-unqualified type
that serves as &quot;value of the same type of the element&quot;, which is commonly just reference type minus cvref qualifiers, but not necessarily (value type and reference type can be completely unrelated, as long as
they have a common reference).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;All of the original descriptions and properties are copied here, credit belongs to the original author.&lt;/p&gt;
&lt;h1&gt;C++20 Range Adaptors&lt;/h1&gt;
&lt;h2&gt;Factories&lt;/h2&gt;
&lt;h3&gt;&lt;code&gt;views::empty&amp;lt;T&amp;gt;: [T&amp;amp;]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Produces an empty range of type &lt;code&gt;T&lt;/code&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;constraint: &lt;code&gt;T&lt;/code&gt; is an object type&lt;/li&gt;
&lt;li&gt;reference: &lt;code&gt;T&amp;amp;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;value type: &lt;code&gt;T&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;category: contiguous&lt;/li&gt;
&lt;li&gt;common: always&lt;/li&gt;
&lt;li&gt;sized: always (0)&lt;/li&gt;
&lt;li&gt;const-iterable: always&lt;/li&gt;
&lt;li&gt;borrowed: always&lt;/li&gt;
&lt;li&gt;constant: when &lt;code&gt;T&lt;/code&gt; is &lt;code&gt;const&lt;/code&gt;-qualified&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;code&gt;views::single(t: T) -&amp;gt; [T&amp;amp;]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Produce a range that only contains a single value: &lt;code&gt;t&lt;/code&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;constraint: &lt;code&gt;T&lt;/code&gt; is an object type&lt;/li&gt;
&lt;li&gt;reference: &lt;code&gt;T&amp;amp;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;value type: &lt;code&gt;T&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;category: contiguous&lt;/li&gt;
&lt;li&gt;common: always&lt;/li&gt;
&lt;li&gt;sized: always (1)&lt;/li&gt;
&lt;li&gt;const-iterable: always&lt;/li&gt;
&lt;li&gt;borrowed: never&lt;/li&gt;
&lt;li&gt;constant: never (&lt;code&gt;single_view&amp;lt;T&amp;gt;&lt;/code&gt; is only instantiated with decayed type)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;code&gt;views::iota(beg: B[, end: E]) -&amp;gt; [B]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Produce a range that start at &lt;code&gt;beg&lt;/code&gt;, and incrementing forever (when there is only one argument) or until &lt;code&gt;beg == end&lt;/code&gt; (exclude &lt;code&gt;end&lt;/code&gt; as usual).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; iota(0)
[0, 1, 2, ...]
&amp;gt;&amp;gt;&amp;gt; iota(0, 5)
[0, 1, 2, 3, 4]
&amp;gt;&amp;gt;&amp;gt; iota(beg, end)
[beg, beg + 1, beg + 2, ..., end - 1]
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;[!TIP]
Note that &lt;code&gt;B&lt;/code&gt; and &lt;code&gt;E&lt;/code&gt; can be any type, not just integral&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;constraint: &lt;code&gt;B&lt;/code&gt; is copyable and &lt;code&gt;weakly_incrementable&lt;/code&gt; (support pre/postfix &lt;code&gt;++&lt;/code&gt; and have difference type) and &lt;code&gt;E&lt;/code&gt; is &lt;code&gt;semiregular&lt;/code&gt; (copyable and default initializable).
Also, &lt;code&gt;beg == end&lt;/code&gt;, &lt;code&gt;beg != end&lt;/code&gt; (and reverse) are valid.&lt;/li&gt;
&lt;li&gt;reference: &lt;code&gt;B&lt;/code&gt; (prvalue range!)&lt;/li&gt;
&lt;li&gt;value type: &lt;code&gt;B&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;category:
&lt;ul&gt;
&lt;li&gt;if &lt;code&gt;B&lt;/code&gt; is advanceable (&lt;code&gt;beg += n&lt;/code&gt;, &lt;code&gt;beg -= n&lt;/code&gt;, &lt;code&gt;beg + n&lt;/code&gt;, &lt;code&gt;n + beg&lt;/code&gt;, &lt;code&gt;beg - n&lt;/code&gt;, &lt;code&gt;beg - beg&lt;/code&gt; are all valid, and &lt;code&gt;B&lt;/code&gt; is totally ordered), random access.&lt;/li&gt;
&lt;li&gt;otherwise, if &lt;code&gt;B&lt;/code&gt; is decrementable (support pre/postfix &lt;code&gt;--&lt;/code&gt;), then bidirectional&lt;/li&gt;
&lt;li&gt;otherwise, if &lt;code&gt;B&lt;/code&gt; is &lt;code&gt;incrementable&lt;/code&gt; (regular and &lt;code&gt;beg++&lt;/code&gt; returns &lt;code&gt;B&lt;/code&gt;), then forward&lt;/li&gt;
&lt;li&gt;otherwise, input.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;common: when &lt;code&gt;B&lt;/code&gt; and &lt;code&gt;E&lt;/code&gt; are the same type&lt;/li&gt;
&lt;li&gt;sized: the range is not infinity (there is a bound provided) and either:
&lt;ul&gt;
&lt;li&gt;the range is common and random access, or&lt;/li&gt;
&lt;li&gt;both &lt;code&gt;B&lt;/code&gt; and &lt;code&gt;E&lt;/code&gt; are integer-like types, or&lt;/li&gt;
&lt;li&gt;&lt;code&gt;E&lt;/code&gt; is a sized sentinel for &lt;code&gt;B&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;const-iterable: always&lt;/li&gt;
&lt;li&gt;borrowed: always (iterator owns the current value)&lt;/li&gt;
&lt;li&gt;constant: when &lt;code&gt;B&lt;/code&gt; is a non-class type (like &lt;code&gt;int&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;code&gt;views::istream&amp;lt;T&amp;gt;(in: In) -&amp;gt; [T&amp;amp;]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Produce a range of &lt;code&gt;T&lt;/code&gt; such that elements are read by &lt;code&gt;in &amp;gt;&amp;gt; t&lt;/code&gt; (read one element per increment).&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;constraint: &lt;code&gt;T&lt;/code&gt; is movable and default initializable, and &lt;code&gt;In&lt;/code&gt; is derived from &lt;code&gt;basic_istream&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;reference: &lt;code&gt;T&amp;amp;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;value type: &lt;code&gt;T&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;category: input&lt;/li&gt;
&lt;li&gt;common: never (iterator are move-only, so not a C++17 input iterator anyway)&lt;/li&gt;
&lt;li&gt;sized: never&lt;/li&gt;
&lt;li&gt;const-iterable: never&lt;/li&gt;
&lt;li&gt;borrowed: never&lt;/li&gt;
&lt;li&gt;constant: never (&lt;code&gt;T&lt;/code&gt; must be movable so it must not be &lt;code&gt;const&lt;/code&gt;-qualified)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;code&gt;views::counted(it: It, n: N) -&amp;gt; [*It]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;This is not a real range adaptor (there is no &lt;code&gt;counted_view&lt;/code&gt;). Instead, it is an adaptor that adapt the range represented as &lt;code&gt;[it, it + n)&lt;/code&gt; (begin + count)
as the standard iterator-sentinel model. It adapts by construct a &lt;code&gt;std::span&lt;/code&gt; or &lt;code&gt;ranges::subrange&lt;/code&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;constraint: &lt;code&gt;n &amp;gt;= 0&lt;/code&gt; and &lt;code&gt;N&lt;/code&gt; must be convertible to the difference type of &lt;code&gt;It&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;reference: same as &lt;code&gt;It&lt;/code&gt;&apos;s reference type&lt;/li&gt;
&lt;li&gt;value type: same as &lt;code&gt;It&lt;/code&gt;&apos;s value type&lt;/li&gt;
&lt;li&gt;category: same as &lt;code&gt;It&lt;/code&gt;&apos;s category (preserve contiguous)&lt;/li&gt;
&lt;li&gt;common: if &lt;code&gt;It&lt;/code&gt; is at least random access&lt;/li&gt;
&lt;li&gt;sized: always&lt;/li&gt;
&lt;li&gt;const-iterable: always&lt;/li&gt;
&lt;li&gt;borrowed: always (&lt;code&gt;counted_iterator&lt;/code&gt; owns the iterator and the count)&lt;/li&gt;
&lt;li&gt;constant: when &lt;code&gt;It&lt;/code&gt; is a constant iterator&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Real Adaptors&lt;/h2&gt;
&lt;h3&gt;&lt;code&gt;views::all(r: [T]) -&amp;gt; [T]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Still, &lt;code&gt;views::all&lt;/code&gt; is a semi-range adaptor; there is no &lt;code&gt;all_view&lt;/code&gt;. Essentially, &lt;code&gt;views::all(r)&lt;/code&gt; is a view of all the elements in &lt;code&gt;r&lt;/code&gt;,
which it done wrapping by either return &lt;code&gt;auto(r)&lt;/code&gt; directly (if &lt;code&gt;r&lt;/code&gt; is already a view), wrap in &lt;code&gt;ref_view&lt;/code&gt; (if &lt;code&gt;r&lt;/code&gt; is a lvalue),
or wrap in &lt;code&gt;owning_view&lt;/code&gt; otherwise. Therefore, all of &lt;code&gt;views::all(r)&lt;/code&gt;&apos;s range properties are exactly identical to that of &lt;code&gt;r&lt;/code&gt;&apos;s.&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;views::filter(r: [T], f: T -&amp;gt; bool) -&amp;gt; [T]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;(Includes DR changes brought by &lt;a href=&quot;https://wg21.link/P3725R3&quot;&gt;P3725R3&lt;/a&gt; in the C++26 cycle.)&lt;/p&gt;
&lt;p&gt;Produce a new range that only preserve elements of &lt;code&gt;r&lt;/code&gt; that let &lt;code&gt;f(e)&lt;/code&gt; evaluate to &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; filter([1, 2, 3, 4], e =&amp;gt; e % 2 == 0)
[2, 4]
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;constraint: &lt;code&gt;F&lt;/code&gt; is copy-constructible, an object type, and is invocable by &lt;code&gt;T&lt;/code&gt; and &lt;code&gt;value_type&amp;amp;&lt;/code&gt;, and return a value that is contextually convertible to &lt;code&gt;bool&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;reference: &lt;code&gt;T&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;value type: same as &lt;code&gt;r&lt;/code&gt;&apos;s value type&lt;/li&gt;
&lt;li&gt;category: at most bidirectional&lt;/li&gt;
&lt;li&gt;common: when &lt;code&gt;r&lt;/code&gt; is common&lt;/li&gt;
&lt;li&gt;sized: never&lt;/li&gt;
&lt;li&gt;const-iterable: when &lt;code&gt;r&lt;/code&gt; is input and not forward&lt;/li&gt;
&lt;li&gt;borrowed: never&lt;/li&gt;
&lt;li&gt;constant: when &lt;code&gt;r&lt;/code&gt; is constant&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;code&gt;views::transform(r: [T], f: T -&amp;gt; U) -&amp;gt; [U]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Return a new range such that each element is &lt;code&gt;f(e)&lt;/code&gt; (where &lt;code&gt;e&lt;/code&gt; is each element in &lt;code&gt;r&lt;/code&gt;). Commonly called &lt;code&gt;map&lt;/code&gt; in other languages.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; transform([&quot;aa&quot;, &quot;bb&quot;, &quot;cc&quot;, &quot;dd&quot;], e =&amp;gt; e[0])
[&apos;a&apos;, &apos;b&apos;, &apos;c&apos;, &apos;d&apos;]
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;constraint: &lt;code&gt;F&lt;/code&gt; is move-constructible, an object type, and is invocable by &lt;code&gt;T&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;reference: &lt;code&gt;U&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;value type: &lt;code&gt;remove_cvref_t&amp;lt;U&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;category: at most random access&lt;/li&gt;
&lt;li&gt;common: when &lt;code&gt;r&lt;/code&gt; is common&lt;/li&gt;
&lt;li&gt;sized: when &lt;code&gt;r&lt;/code&gt; is sized&lt;/li&gt;
&lt;li&gt;const-iterable: when &lt;code&gt;r&lt;/code&gt; is const-iterable and &lt;code&gt;f&lt;/code&gt; is const-invocable&lt;/li&gt;
&lt;li&gt;borrowed: never&lt;/li&gt;
&lt;li&gt;constant: when &lt;code&gt;U&lt;/code&gt; is a value of non-class type (like prvalue range of &lt;code&gt;int&lt;/code&gt;) or a const reference (l/rvalue both applies)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;code&gt;views::take(r: [T], n: N) -&amp;gt; [T]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Produce a new range consists of the first &lt;code&gt;n&lt;/code&gt; elements of &lt;code&gt;r&lt;/code&gt;. If &lt;code&gt;r&lt;/code&gt; has less than &lt;code&gt;n&lt;/code&gt; elements, contains all of &lt;code&gt;r&lt;/code&gt;&apos;s elements.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; take([1, 2, 3, 4], 2)
[1, 2]
&amp;gt;&amp;gt;&amp;gt; take([1, 2, 3, 4], 8)
[1, 2, 3, 4]
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;[!NOTE]
&lt;code&gt;views::take&lt;/code&gt; will produce &lt;code&gt;r&lt;/code&gt;&apos;s type whenever possible (for example, &lt;code&gt;empty_view&lt;/code&gt; passed in will return an &lt;code&gt;empty_view&lt;/code&gt;).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;constraint: &lt;code&gt;n &amp;gt;= 0&lt;/code&gt; and &lt;code&gt;N&lt;/code&gt; is convertible to &lt;code&gt;r&lt;/code&gt;&apos;s difference type&lt;/li&gt;
&lt;li&gt;reference: &lt;code&gt;T&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;value type: same as &lt;code&gt;r&lt;/code&gt;&apos;s value type&lt;/li&gt;
&lt;li&gt;category: same as &lt;code&gt;r&lt;/code&gt; (preserve contiguous)&lt;/li&gt;
&lt;li&gt;common: when &lt;code&gt;r&lt;/code&gt; is sized and random access&lt;/li&gt;
&lt;li&gt;sized: when &lt;code&gt;r&lt;/code&gt; is sized&lt;/li&gt;
&lt;li&gt;const-iterable: when &lt;code&gt;r&lt;/code&gt; is const-iterable&lt;/li&gt;
&lt;li&gt;borrowed: when &lt;code&gt;r&lt;/code&gt; is borrowed&lt;/li&gt;
&lt;li&gt;constant: when &lt;code&gt;r&lt;/code&gt; is constant&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;code&gt;views::take_while(r: [T], f: T -&amp;gt; bool) -&amp;gt; [T]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Produce a new range that includes all the element of &lt;code&gt;r&lt;/code&gt; that makes &lt;code&gt;f(e)&lt;/code&gt; evaluates to &lt;code&gt;true&lt;/code&gt; until it first evaluates to &lt;code&gt;false&lt;/code&gt;.
(i.e. filter but stop when first &lt;code&gt;false&lt;/code&gt;)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; take_while([1, 2, 3, 1, 2, 3], e =&amp;gt; e &amp;lt; 3)
[1, 2]
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;constraint: &lt;code&gt;F&lt;/code&gt; is an object type and is const-invocable by &lt;code&gt;T&lt;/code&gt; and &lt;code&gt;value_type&amp;amp;&lt;/code&gt;, and return a value that is contextually convertible to &lt;code&gt;bool&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;reference: &lt;code&gt;T&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;value type: same as &lt;code&gt;r&lt;/code&gt;&apos;s value type&lt;/li&gt;
&lt;li&gt;category: same as &lt;code&gt;r&lt;/code&gt; (preserve contiguous)&lt;/li&gt;
&lt;li&gt;common: never (&lt;code&gt;begin()&lt;/code&gt; must return an iterator-to-&lt;code&gt;r&lt;/code&gt;, so &lt;code&gt;end()&lt;/code&gt; cannot reuse that iterator type)&lt;/li&gt;
&lt;li&gt;sized: never&lt;/li&gt;
&lt;li&gt;const-iterable: when &lt;code&gt;r&lt;/code&gt; is const-iterable and &lt;code&gt;f&lt;/code&gt; is const-invocable by the reference and lvalue of value type of &lt;code&gt;as_const(r)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;borrowed: never&lt;/li&gt;
&lt;li&gt;constant: when &lt;code&gt;r&lt;/code&gt; is constant&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;code&gt;views::drop(r: [T], n: N) -&amp;gt; [T]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Produce a new range consists of the all but the first &lt;code&gt;n&lt;/code&gt; elements of &lt;code&gt;r&lt;/code&gt;. If &lt;code&gt;r&lt;/code&gt; has less than &lt;code&gt;n&lt;/code&gt; elements, produce an empty range.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; drop([1, 2, 3, 4], 2)
[3, 4]
&amp;gt;&amp;gt;&amp;gt; drop([1, 2, 3, 4], 8)
[]
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;[!NOTE]
&lt;code&gt;views::drop&lt;/code&gt; will produce &lt;code&gt;r&lt;/code&gt;&apos;s type whenever possible (for example, &lt;code&gt;empty_view&lt;/code&gt; passed in will return an &lt;code&gt;empty_view&lt;/code&gt;).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;constraint: &lt;code&gt;n &amp;gt;= 0&lt;/code&gt; and &lt;code&gt;N&lt;/code&gt; is convertible to &lt;code&gt;r&lt;/code&gt;&apos;s difference type&lt;/li&gt;
&lt;li&gt;reference: &lt;code&gt;T&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;value type: same as &lt;code&gt;r&lt;/code&gt;&apos;s value type&lt;/li&gt;
&lt;li&gt;category: same as &lt;code&gt;r&lt;/code&gt; (preserve contiguous)&lt;/li&gt;
&lt;li&gt;common: when &lt;code&gt;r&lt;/code&gt; is common&lt;/li&gt;
&lt;li&gt;sized: when &lt;code&gt;r&lt;/code&gt; is sized&lt;/li&gt;
&lt;li&gt;const-iterable: when &lt;code&gt;r&lt;/code&gt; is const-iterable&lt;/li&gt;
&lt;li&gt;borrowed: when &lt;code&gt;r&lt;/code&gt; is borrowed&lt;/li&gt;
&lt;li&gt;constant: when &lt;code&gt;r&lt;/code&gt; is constant&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;code&gt;views::drop_while(r: [T], f: T -&amp;gt; bool) -&amp;gt; [T]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Produce a new range that excludes the element of &lt;code&gt;r&lt;/code&gt; until the first element that makes &lt;code&gt;f(e)&lt;/code&gt; evaluates to &lt;code&gt;false&lt;/code&gt;.
(i.e. drop but stop when first &lt;code&gt;false&lt;/code&gt;)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; drop_while([1, 2, 3, 1, 2, 3], e =&amp;gt; e &amp;lt; 3)
[3, 1, 2, 3]
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;constraint: &lt;code&gt;F&lt;/code&gt; is an object type and is const-invocable by &lt;code&gt;T&lt;/code&gt; and &lt;code&gt;value_type&amp;amp;&lt;/code&gt;, and return a value that is contextually convertible to &lt;code&gt;bool&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;reference: &lt;code&gt;T&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;value type: same as &lt;code&gt;r&lt;/code&gt;&apos;s value type&lt;/li&gt;
&lt;li&gt;category: same as &lt;code&gt;r&lt;/code&gt; (preserve contiguous)&lt;/li&gt;
&lt;li&gt;common: when &lt;code&gt;r&lt;/code&gt; is common&lt;/li&gt;
&lt;li&gt;sized: when &lt;code&gt;R&lt;/code&gt;&apos;s sentinel is a sized sentinel for &lt;code&gt;R&lt;/code&gt;&apos;s iterator&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;[!NOTE]
In common case this requires common &amp;amp; random access range, but not necessarily; the requirement is &lt;code&gt;s - i&lt;/code&gt; and &lt;code&gt;i - s&lt;/code&gt; are valid and return the difference type.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;const-iterable: never&lt;/li&gt;
&lt;li&gt;borrowed: when &lt;code&gt;r&lt;/code&gt; is borrowed&lt;/li&gt;
&lt;li&gt;constant: when &lt;code&gt;r&lt;/code&gt; is constant&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;code&gt;views::join(r: [[T]]) -&amp;gt; [T]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Join together a range of several range-of-&lt;code&gt;T&lt;/code&gt;s into a single range-of-&lt;code&gt;T&lt;/code&gt;. Commonly called &lt;code&gt;flatten&lt;/code&gt; in other languages.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; join([[1, 2], [3], [4, 5, 6]])
[1, 2, 3, 4, 5, 6]
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;constraint: &lt;code&gt;r&lt;/code&gt; and &lt;code&gt;R&lt;/code&gt;&apos;s reference type (&lt;code&gt;[T]&lt;/code&gt;) are both input ranges&lt;/li&gt;
&lt;li&gt;reference: &lt;code&gt;T&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;value type: same as &lt;code&gt;[T]&lt;/code&gt;&apos;s value type (the value type of inner range)&lt;/li&gt;
&lt;li&gt;category:
&lt;ul&gt;
&lt;li&gt;If &lt;code&gt;r&lt;/code&gt; is a range of glvalue ranges, then at most bidirectional based on inner range&apos;s category&lt;/li&gt;
&lt;li&gt;Otherwise (range of prvalue ranges), input&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;common: when both &lt;code&gt;r&lt;/code&gt; and inner range are forward and common, and &lt;code&gt;r&lt;/code&gt; is a range of glvalue ranges&lt;/li&gt;
&lt;li&gt;sized: never&lt;/li&gt;
&lt;li&gt;const-iterable: when &lt;code&gt;r&lt;/code&gt; is const-iterable and is a range of glvalue ranges&lt;/li&gt;
&lt;li&gt;borrowed: never&lt;/li&gt;
&lt;li&gt;constant: when inner range is constant&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;code&gt;views::lazy_split(r: [T], p: T | [T]) -&amp;gt; [[T]]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;(The fixed version after C++20 DR &lt;a href=&quot;https://wg21.link/P2210R2&quot;&gt;P2210R2&lt;/a&gt;)
Produce a range that splits a range of &lt;code&gt;T&lt;/code&gt; into a range of several range-of-&lt;code&gt;T&lt;/code&gt;s based on delimeter (which can be a single element or a continuous subrange).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; lazy_split(&quot;a bc def&quot;, &apos; &apos;)
[&quot;a&quot;, &quot;bc&quot;, &quot;def&quot;]
&amp;gt;&amp;gt;&amp;gt; lazy_split(&quot;a||b|c||d&quot;, &quot;||&quot;)
[&quot;a&quot;, &quot;b|c&quot;, &quot;d&quot;]
&amp;gt;&amp;gt;&amp;gt; lazy_split(&quot;abcd&quot;, &quot;&quot;)  # when size = 0, just split at every element
[&quot;a&quot;, &quot;b&quot;, &quot;c&quot;, &quot;d&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;lazy_split&lt;/code&gt; is maximally lazy, and it will never touch any element until you increment to the element (i.e. will not compute any &quot;next pattern position&quot;),
and thus support input ranges. However, the tradeoff is that the resulting inner range can only be at most forward, as you don&apos;t really know you are at the end until you increment here.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;constraint: &lt;code&gt;p&lt;/code&gt; is either a forward range or convertible to the value type of &lt;code&gt;R&lt;/code&gt;. Also, when &lt;code&gt;r&lt;/code&gt; is only an input range, &lt;code&gt;p&lt;/code&gt; must be a sized range with size 0 or 1 (nothing or a single element).
(The reference type and lvalues of values of &lt;code&gt;P&lt;/code&gt; and &lt;code&gt;R&lt;/code&gt; also must be inter-comparable)&lt;/li&gt;
&lt;li&gt;reference: &lt;code&gt;[T]&lt;/code&gt; (&lt;code&gt;lazy_split_view::value_type&lt;/code&gt;, a range with reference type &lt;code&gt;T&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;value type: outer range same as reference, inner range same as &lt;code&gt;r&lt;/code&gt;&apos;s value type&lt;/li&gt;
&lt;li&gt;category: both outer range and inner range is at most forward based on &lt;code&gt;R&lt;/code&gt;&apos;s category (for a stronger inner range, see &lt;code&gt;views::split&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;common: outer range when &lt;code&gt;r&lt;/code&gt; is forward and common, inner range never&lt;/li&gt;
&lt;li&gt;sized: (both outer and inner range) never&lt;/li&gt;
&lt;li&gt;const-iterable: outer range when &lt;code&gt;r&lt;/code&gt; is const-iterable and both &lt;code&gt;R&lt;/code&gt; and &lt;code&gt;const R&lt;/code&gt; are forward ranges; inner range always&lt;/li&gt;
&lt;li&gt;borrowed: (both outer and inner range) never&lt;/li&gt;
&lt;li&gt;constant: outer range never, inner range when &lt;code&gt;r&lt;/code&gt; is constant&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;code&gt;views::split(r: [T], p: T | [T]) -&amp;gt; [[T]]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;(The fixed version after C++20 DR &lt;a href=&quot;https://wg21.link/P2210R2&quot;&gt;P2210R2&lt;/a&gt;)
Produce a range that splits a range of &lt;code&gt;T&lt;/code&gt; into a range of several range-of-&lt;code&gt;T&lt;/code&gt;s based on delimeter (which can be a single element or a continuous subrange).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; split(&quot;a bc def&quot;, &apos; &apos;)
[&quot;a&quot;, &quot;bc&quot;, &quot;def&quot;]
&amp;gt;&amp;gt;&amp;gt; split(&quot;a||b|c||d&quot;, &quot;||&quot;)
[&quot;a&quot;, &quot;b|c&quot;, &quot;d&quot;]
&amp;gt;&amp;gt;&amp;gt; split(&quot;abcd&quot;, &quot;&quot;)  # when size = 0, just split at every element
[&quot;a&quot;, &quot;b&quot;, &quot;c&quot;, &quot;d&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;split&lt;/code&gt; is still lazy, but it eagerly computes the start of next subrange when iterating, thus does not support input range but allow subrange to be at most contiguous.
(Since input range is rare and most string algorithm require more than forward range, this should be used in most times)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;constraint: &lt;code&gt;r&lt;/code&gt; must be a forward range (for splitting input range, use &lt;code&gt;lazy_split&lt;/code&gt;), &lt;code&gt;p&lt;/code&gt; is either a forward range or convertible to the value type of &lt;code&gt;R&lt;/code&gt;.
(The reference type and lvalues of values of &lt;code&gt;P&lt;/code&gt; and &lt;code&gt;R&lt;/code&gt; also must be inter-comparable)&lt;/li&gt;
&lt;li&gt;reference: &lt;code&gt;[T]&lt;/code&gt; (specifically, the reference type is precisely &lt;code&gt;ranges::subrange&amp;lt;iterator_t&amp;lt;R&amp;gt;&amp;gt;&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;value type: same as reference type (inner range&apos;s value type same as &lt;code&gt;r&lt;/code&gt;&apos;s)&lt;/li&gt;
&lt;li&gt;category: outer range forward, inner range same as &lt;code&gt;r&lt;/code&gt; (preserve contiguous)&lt;/li&gt;
&lt;li&gt;common: outer range when &lt;code&gt;r&lt;/code&gt; is common, inner range always&lt;/li&gt;
&lt;li&gt;sized: outer range never, inner range when &lt;code&gt;r&lt;/code&gt;&apos;s sentinel is a sized sentinel (common case is when &lt;code&gt;r&lt;/code&gt; is random access)&lt;/li&gt;
&lt;li&gt;const-iterable: outer range never, inner range when &lt;code&gt;r&lt;/code&gt; is const-iterable&lt;/li&gt;
&lt;li&gt;borrowed: outer range never, inner range always&lt;/li&gt;
&lt;li&gt;constant: outer range never, inner range when &lt;code&gt;r&lt;/code&gt; is constant&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;code&gt;views::common(r: [T]) -&amp;gt; [T]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Produce a range with same element as in &lt;code&gt;r&lt;/code&gt;, but ensure that the result is a common range.
(Basically exists as a compatibility layer so that pre-C++20 iterator-pair algorithms can use C++20 ranges)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;constraint: either &lt;code&gt;r&lt;/code&gt; is common, or iterators of &lt;code&gt;R&lt;/code&gt; must be copyable&lt;/li&gt;
&lt;li&gt;reference: &lt;code&gt;T&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;value type: same as &lt;code&gt;r&lt;/code&gt;&apos;s value type&lt;/li&gt;
&lt;li&gt;category: if &lt;code&gt;r&lt;/code&gt; is common or both random access and sized, then same as &lt;code&gt;r&lt;/code&gt; (preserve contiguous); otherwise at most forward&lt;/li&gt;
&lt;li&gt;common: always (this is the point of this adaptor)&lt;/li&gt;
&lt;li&gt;sized: when &lt;code&gt;r&lt;/code&gt; is sized&lt;/li&gt;
&lt;li&gt;const-iterable: when &lt;code&gt;r&lt;/code&gt; is const-iterable&lt;/li&gt;
&lt;li&gt;borrowed: when &lt;code&gt;r&lt;/code&gt; is borrowed&lt;/li&gt;
&lt;li&gt;constant: when &lt;code&gt;r&lt;/code&gt; is constant&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;code&gt;views::reverse(r: [T]) -&amp;gt; [T]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Produce a range that contains the reverse of the elements in &lt;code&gt;r&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; reverse([1, 2, 3])
[3, 2, 1]
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;[!TIP]
Note that the reverse of &lt;code&gt;reverse_view&lt;/code&gt; is simply the base range itself, and &lt;code&gt;subrange&lt;/code&gt; passed-in will return &lt;code&gt;subrange&lt;/code&gt; too.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;constraint: &lt;code&gt;r&lt;/code&gt; is at least bidirectional&lt;/li&gt;
&lt;li&gt;reference: &lt;code&gt;T&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;value type: same as &lt;code&gt;r&lt;/code&gt;&apos;s value type&lt;/li&gt;
&lt;li&gt;category: at most random access&lt;/li&gt;
&lt;li&gt;common: always&lt;/li&gt;
&lt;li&gt;sized: when &lt;code&gt;r&lt;/code&gt; is sized&lt;/li&gt;
&lt;li&gt;const-iterable: when &lt;code&gt;r&lt;/code&gt; is const-iterable and &lt;code&gt;const R&lt;/code&gt; is common&lt;/li&gt;
&lt;li&gt;borrowed: when &lt;code&gt;r&lt;/code&gt; is borrowed&lt;/li&gt;
&lt;li&gt;constant: when &lt;code&gt;r&lt;/code&gt; is constant&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;code&gt;views::elements&amp;lt;I: size_t&amp;gt;(r: [(T1, T2, ..., TN)]) -&amp;gt; [TI]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Produce a range consists of the &lt;code&gt;I&lt;/code&gt;-th element of each element (which are tuples).
&lt;code&gt;views::keys&lt;/code&gt; is equivalent to &lt;code&gt;views::element&amp;lt;0&amp;gt;&lt;/code&gt;, and &lt;code&gt;views::values&lt;/code&gt; is equivalent to &lt;code&gt;views::element&amp;lt;1&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; r = [(&quot;A&quot;, 1), (&quot;B&quot;, 2)]
&amp;gt;&amp;gt;&amp;gt; elements&amp;lt;1&amp;gt;(r)  # or values(r)
[1, 2]
&amp;gt;&amp;gt;&amp;gt; keys(r)  # or elements&amp;lt;0&amp;gt;(r)
[&quot;A&quot;, &quot;B&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;constraint: &lt;code&gt;r&lt;/code&gt;&apos;s reference type &lt;code&gt;T = (T1, T2, ..., TN)&lt;/code&gt; (minus reference) and value type are &lt;a href=&quot;https://wg21.link/P2165&quot;&gt;tuple-like&lt;/a&gt; types with size larger than &lt;code&gt;I&lt;/code&gt;,
and either &lt;code&gt;T&lt;/code&gt; is a true reference (not prvalue/proxy range), or the &lt;code&gt;I&lt;/code&gt;-th type in the tuple is move constructible.&lt;/li&gt;
&lt;li&gt;reference: &lt;code&gt;TI&lt;/code&gt; (the return type of &lt;code&gt;std::get&amp;lt;I&amp;gt;(e)&lt;/code&gt; where &lt;code&gt;e&lt;/code&gt; is an element of &lt;code&gt;r&lt;/code&gt;), with &lt;code&gt;R&lt;/code&gt;&apos;s cvref qualifier copied onto&lt;/li&gt;
&lt;li&gt;value type: &lt;code&gt;remove_cvref_t&amp;lt;reference&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;category: at most random access&lt;/li&gt;
&lt;li&gt;common: when &lt;code&gt;r&lt;/code&gt; is common&lt;/li&gt;
&lt;li&gt;sized: when &lt;code&gt;r&lt;/code&gt; is sized&lt;/li&gt;
&lt;li&gt;const-iterable: when &lt;code&gt;r&lt;/code&gt; is const-iterable&lt;/li&gt;
&lt;li&gt;borrowed: when &lt;code&gt;r&lt;/code&gt; is borrowed&lt;/li&gt;
&lt;li&gt;constant: when &lt;code&gt;r&lt;/code&gt; is constant (?)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Other Standard Views&lt;/h2&gt;
&lt;p&gt;(&lt;code&gt;std::initializer_list&amp;lt;T&amp;gt;&lt;/code&gt; is technically a view, but it does not model &lt;code&gt;ranges::view&lt;/code&gt;.)&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;std::basic_string_view&amp;lt;charT[, traits[, Alloc]]&amp;gt;: [charT&amp;amp;]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;A lightweight view of a constant contiguous sequence of &lt;code&gt;charT&lt;/code&gt;s (i.e. a string). Can view &lt;code&gt;const charT*&lt;/code&gt;, &lt;code&gt;std::basic_string&lt;/code&gt;, and many more.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;constraint: &lt;code&gt;charT&lt;/code&gt; must be char-like (non-array trivial standard-layout type), and &lt;code&gt;traits&lt;/code&gt; must be a character trait&lt;/li&gt;
&lt;li&gt;reference: &lt;code&gt;charT&amp;amp;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;value type: &lt;code&gt;charT&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;category: contiguous&lt;/li&gt;
&lt;li&gt;common: always&lt;/li&gt;
&lt;li&gt;sized: always&lt;/li&gt;
&lt;li&gt;const-iterable: always&lt;/li&gt;
&lt;li&gt;borrowed: always&lt;/li&gt;
&lt;li&gt;constant: always&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;code&gt;std::span&amp;lt;T[, extent: size_t]&amp;gt;: [T&amp;amp;]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;A lightweight view of a contiguous sequence of &lt;code&gt;T&lt;/code&gt;s). Can view &lt;code&gt;T*&lt;/code&gt;, so a replacement of traditional &lt;code&gt;T*&lt;/code&gt; + length idiom.
A &lt;code&gt;span&amp;lt;T&amp;gt;&lt;/code&gt; is by default with dynamic extent, and &lt;code&gt;span&amp;lt;T, extent&amp;gt;&lt;/code&gt; is a view of fixed size.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;constraint: &lt;code&gt;T&lt;/code&gt; must be a complete object type that is not abstract.&lt;/li&gt;
&lt;li&gt;reference: &lt;code&gt;T&amp;amp;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;value type: &lt;code&gt;remove_cvref_t&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;category: contiguous&lt;/li&gt;
&lt;li&gt;common: always&lt;/li&gt;
&lt;li&gt;sized: always&lt;/li&gt;
&lt;li&gt;const-iterable: always&lt;/li&gt;
&lt;li&gt;borrowed: always&lt;/li&gt;
&lt;li&gt;constant: when &lt;code&gt;T&lt;/code&gt; is &lt;code&gt;const&lt;/code&gt;-qualified&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;C++23 Range Adaptors&lt;/h1&gt;
&lt;p&gt;These are the range adaptors available in C++23 DIS.&lt;/p&gt;
&lt;h2&gt;Factories&lt;/h2&gt;
&lt;h3&gt;&lt;code&gt;views::zip(r1: [T1], r2: [T2], ...) -&amp;gt; [(T1, T2, ...)]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Produce a new range that is &lt;code&gt;r1&lt;/code&gt;, &lt;code&gt;r2&lt;/code&gt;, ... zipped together; i.e. a range of tuple of each corresponding elements in each of the argument ranges.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; zip([1, 2, 3], [4, 5, 6])
[(1, 4), (2, 5), (3, 6)]
&amp;gt;&amp;gt;&amp;gt; zip([1, 2], [&quot;A&quot;, &quot;B&quot;], [1.0, 2.0])
[(1, &quot;A&quot;, 1.0), (2, &quot;B&quot;, 2.0)]
&amp;gt;&amp;gt;&amp;gt; zip()
[]  # empty view with type tuple&amp;lt;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;constraint: all the ranges are input&lt;/li&gt;
&lt;li&gt;reference: &lt;code&gt;tuple&amp;lt;T1, T2, ...&amp;gt;&lt;/code&gt; (note that &lt;code&gt;TI&lt;/code&gt; is the reference type)&lt;/li&gt;
&lt;li&gt;value type: &lt;code&gt;tuple&amp;lt;range_value_t&amp;lt;R1&amp;gt;, ...&amp;gt;&lt;/code&gt; (&lt;strong&gt;not&lt;/strong&gt; the reference type minus reference)&lt;/li&gt;
&lt;li&gt;category: the weakest category in all of &lt;code&gt;r1&lt;/code&gt;, &lt;code&gt;r2&lt;/code&gt;, ..., and also at most random access&lt;/li&gt;
&lt;li&gt;common: when either of following is true:
&lt;ul&gt;
&lt;li&gt;there is only one range (&lt;code&gt;zip(r)&lt;/code&gt;) and this range is common, or&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;zip_view&lt;/code&gt; is not bidirectional (i.e. at most forward), and all of &lt;code&gt;r1&lt;/code&gt;, &lt;code&gt;r2&lt;/code&gt;, ... are common, or&lt;/li&gt;
&lt;li&gt;all of &lt;code&gt;r1&lt;/code&gt;, &lt;code&gt;r2&lt;/code&gt;, ... are both random access and sized&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;sized: when all of &lt;code&gt;r1&lt;/code&gt;, &lt;code&gt;r2&lt;/code&gt;, ... are sized&lt;/li&gt;
&lt;li&gt;const-iterable: when all of &lt;code&gt;r1&lt;/code&gt;, &lt;code&gt;r2&lt;/code&gt;, ... are const-iterable&lt;/li&gt;
&lt;li&gt;borrowed: when all of &lt;code&gt;r1&lt;/code&gt;, &lt;code&gt;r2&lt;/code&gt;, ... are borrowed&lt;/li&gt;
&lt;li&gt;constant: when all of &lt;code&gt;r1&lt;/code&gt;, &lt;code&gt;r2&lt;/code&gt;, ... are constant&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;code&gt;views::zip_transform(f: (T1, T2, ...) -&amp;gt; U, r1: [T1], r2: [T2], ...) -&amp;gt; [U]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Produce a new range in which each element is &lt;code&gt;f(e1, e2, ...)&lt;/code&gt; where &lt;code&gt;e1&lt;/code&gt;, &lt;code&gt;e2&lt;/code&gt; is the corresponding element of &lt;code&gt;r1&lt;/code&gt;, &lt;code&gt;r2&lt;/code&gt;, ... respectfully.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; zip_transform(f, [1, 2, 3], [4, 5, 6])
[f(1, 4), f(2, 5), f(3, 6)]
&amp;gt;&amp;gt;&amp;gt; zip_transform((a, b, c) =&amp;gt; to_string(a) + b + to_string(c), [1, 2], [&quot;A&quot;, &quot;B&quot;], [1.0, 2.0])
[&quot;1A1.0&quot;, &quot;2B2.0&quot;]
&amp;gt;&amp;gt;&amp;gt; zip_transform(f)
[]  # empty view with the type of result of f()
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;constraint: &lt;code&gt;F&lt;/code&gt; is move constructible and is an object type, is invocable by &lt;code&gt;T1, T2, ...&lt;/code&gt;, and all the ranges are input&lt;/li&gt;
&lt;li&gt;reference: &lt;code&gt;U&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;value type: &lt;code&gt;remove_cvref_t&amp;lt;U&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;category: if &lt;code&gt;f(e1, e2, ...)&lt;/code&gt; does not return a lvalue reference, input; otherwise the weakest category in all of &lt;code&gt;r1&lt;/code&gt;, &lt;code&gt;r2&lt;/code&gt;, ..., and also at most random access&lt;/li&gt;
&lt;li&gt;common: when &lt;code&gt;zip(r1, r2, ...)&lt;/code&gt; is common&lt;/li&gt;
&lt;li&gt;sized: when all of &lt;code&gt;r1&lt;/code&gt;, &lt;code&gt;r2&lt;/code&gt;, ... are sized&lt;/li&gt;
&lt;li&gt;const-iterable: when all of &lt;code&gt;r1&lt;/code&gt;, &lt;code&gt;r2&lt;/code&gt;, ... are const-iterable and &lt;code&gt;f&lt;/code&gt; is const-invocable&lt;/li&gt;
&lt;li&gt;borrowed: never&lt;/li&gt;
&lt;li&gt;constant: when the result of &lt;code&gt;f(e1, e2, ...)&lt;/code&gt; is a non-class or &lt;code&gt;std::tuple&lt;/code&gt; (pr)value, or when it returns a constant reference&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;code&gt;views::cartesian_product(r1: [T1], r2: [T2], ...) -&amp;gt; [(T1, T2, ...)]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Produce a new range that is &lt;code&gt;r1&lt;/code&gt;, &lt;code&gt;r2&lt;/code&gt;, ... cartesian producted together; i.e. a range of tuple of every possible pair of elements in each of the argument ranges.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; cartesian_product([1, 2, 3], [4, 5, 6])
[(1, 4), (1, 5), (1, 6), (2, 4), (2, 5), (2, 6), (3, 4), (3, 5), (3, 6)]
&amp;gt;&amp;gt;&amp;gt; cartesian_product([1, 2], [&quot;A&quot;, &quot;B&quot;], [1.0, 2.0])
[(1, &quot;A&quot;, 1.0), (1, &quot;A&quot;, 2.0), (1, &quot;B&quot;, 1.0), (1, &quot;B&quot;, 2.0),
 (2, &quot;A&quot;, 1.0), (2, &quot;A&quot;, 2.0), (2, &quot;B&quot;, 1.0), (2, &quot;B&quot;, 2.0)]
&amp;gt;&amp;gt;&amp;gt; cartesian_product()
[()]  # views::single(std::tuple&amp;lt;&amp;gt;{})
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;constraint: all of &lt;code&gt;r2, r3, ...&lt;/code&gt; (all ranges except the first one) are forward&lt;/li&gt;
&lt;li&gt;reference: &lt;code&gt;tuple&amp;lt;T1, T2, ...&amp;gt;&lt;/code&gt; (note that &lt;code&gt;TI&lt;/code&gt; is the reference type)&lt;/li&gt;
&lt;li&gt;value type: &lt;code&gt;tuple&amp;lt;range_value_t&amp;lt;R1&amp;gt;, ...&amp;gt;&lt;/code&gt; (&lt;strong&gt;not&lt;/strong&gt; the reference type minus reference)&lt;/li&gt;
&lt;li&gt;category:
&lt;ul&gt;
&lt;li&gt;if the first range &lt;code&gt;r1&lt;/code&gt; is random access, and all other ranges are both random access and sized, then random access&lt;/li&gt;
&lt;li&gt;otherwise, if the first range &lt;code&gt;r1&lt;/code&gt; is bidirectional, and all other ranges are either both bidirectional and common, or both random access and sized, then bidirectional&lt;/li&gt;
&lt;li&gt;otherwise, if the first range &lt;code&gt;r1&lt;/code&gt; is forward, then forward&lt;/li&gt;
&lt;li&gt;otherwise, input&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;common: when &lt;code&gt;r1&lt;/code&gt; is common, or is both sized and random access&lt;/li&gt;
&lt;li&gt;sized: when all of &lt;code&gt;r1&lt;/code&gt;, &lt;code&gt;r2&lt;/code&gt;, ... are sized&lt;/li&gt;
&lt;li&gt;const-iterable: when all of &lt;code&gt;r1&lt;/code&gt;, &lt;code&gt;r2&lt;/code&gt;, ... are const-iterable&lt;/li&gt;
&lt;li&gt;borrowed: never&lt;/li&gt;
&lt;li&gt;constant: when all of &lt;code&gt;r1&lt;/code&gt;, &lt;code&gt;r2&lt;/code&gt;, ... are constant&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;code&gt;views::repeat(t: T[, n: N]) -&amp;gt; [const T&amp;amp;]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Produce a range that repeats the same value &lt;code&gt;t&lt;/code&gt; either infinitely (when there is only one argument), or for &lt;code&gt;n&lt;/code&gt; times.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; repeat(2)
[2, 2, 2, ...]
&amp;gt;&amp;gt;&amp;gt; repeat(2, 5)
[2, 2, 2, 2, 2]
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;constraint: &lt;code&gt;T&lt;/code&gt; is move constructible and is an object type; &lt;code&gt;N&lt;/code&gt; (if provided) is a semiregular integer-like type&lt;/li&gt;
&lt;li&gt;reference: &lt;code&gt;const T&amp;amp;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;value type: &lt;code&gt;T&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;category: random access&lt;/li&gt;
&lt;li&gt;common: when the resulting range is not infinite (i.e. when &lt;code&gt;n&lt;/code&gt; is provided)&lt;/li&gt;
&lt;li&gt;sized: when the resulting range is not infinite (i.e. when &lt;code&gt;n&lt;/code&gt; is provided)&lt;/li&gt;
&lt;li&gt;const-iterable: always&lt;/li&gt;
&lt;li&gt;borrowed: never&lt;/li&gt;
&lt;li&gt;constant: always&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Real Adaptors&lt;/h2&gt;
&lt;h3&gt;&lt;code&gt;views::as_rvalue(r: [T]) -&amp;gt; [T&amp;amp;&amp;amp;]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Produce a range with same element as in &lt;code&gt;r&lt;/code&gt;, but ensure that the result is a range of rvalue reference.
(Basically, did a &lt;code&gt;std::move&lt;/code&gt; on each element so that you can then move every element from the view into some container or things like that)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;constraint: &lt;code&gt;r&lt;/code&gt; is an input range&lt;/li&gt;
&lt;li&gt;reference: &lt;code&gt;range_rvalue_reference_t&amp;lt;R&amp;gt;&lt;/code&gt; (which normally is the result type of &lt;code&gt;std::move(*r.begin())&lt;/code&gt; so basically &lt;code&gt;T&amp;amp;&amp;amp;&lt;/code&gt;, but you can customize it by &lt;code&gt;ranges::iter_move&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;value type: same as &lt;code&gt;r&lt;/code&gt;&apos;s value type&lt;/li&gt;
&lt;li&gt;category: at most random access (the original intent is that &lt;code&gt;as_rvalue_view&lt;/code&gt; should be input-only, but later &lt;a href=&quot;https://wg21.link/P2520R0&quot;&gt;P2520R0&lt;/a&gt; changed &lt;code&gt;as_rvalue_view&lt;/code&gt; to now be up to random access)&lt;/li&gt;
&lt;li&gt;common: when &lt;code&gt;r&lt;/code&gt; is common&lt;/li&gt;
&lt;li&gt;sized: when &lt;code&gt;r&lt;/code&gt; is sized&lt;/li&gt;
&lt;li&gt;const-iterable: when &lt;code&gt;r&lt;/code&gt; is const-iterable&lt;/li&gt;
&lt;li&gt;borrowed: when &lt;code&gt;r&lt;/code&gt; is borrowed&lt;/li&gt;
&lt;li&gt;constant: when &lt;code&gt;r&lt;/code&gt; is constant&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;code&gt;views::join_with(r: [[T]], p: U | [U]) -&amp;gt; [common_reference_t&amp;lt;T, U&amp;gt;]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Join together a range of several range-of-&lt;code&gt;T&lt;/code&gt;s into a single range-of-&lt;code&gt;T&lt;/code&gt;, with &lt;code&gt;p&lt;/code&gt; inserted between each parts. This is the reverse of &lt;code&gt;views::split&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; join_with([[1, 2], [3], [4, 5, 6]], 3)
[1, 2, 3, 3, 3, 4, 5, 6]
&amp;gt;&amp;gt;&amp;gt; join_with([[1, 2], [3], [4, 5, 6]], [3, 4])
[1, 2, 3, 4, 3, 3, 4, 4, 5, 6]
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;constraint: &lt;code&gt;r&lt;/code&gt; and &lt;code&gt;R&lt;/code&gt;&apos;s reference type (&lt;code&gt;[T]&lt;/code&gt;) are both input ranges, &lt;code&gt;p&lt;/code&gt; is either a forward range or convertible to the value type of &lt;code&gt;R&lt;/code&gt;.
(inner range and &lt;code&gt;p&lt;/code&gt;&apos;s value and reference type must also have common reference)&lt;/li&gt;
&lt;li&gt;reference: &lt;code&gt;common_reference_t&amp;lt;T, U&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;value type: the &lt;code&gt;common_type_t&lt;/code&gt; of inner range and &lt;code&gt;p&lt;/code&gt;&apos;s value type&lt;/li&gt;
&lt;li&gt;category:
&lt;ul&gt;
&lt;li&gt;If &lt;code&gt;r&lt;/code&gt; is a range of prvalue ranges, then input&lt;/li&gt;
&lt;li&gt;Otherwise (&lt;code&gt;r&lt;/code&gt; is a range of glvalue ranges), and if &lt;code&gt;r&lt;/code&gt; is bidirectional, and inner range and &lt;code&gt;p&lt;/code&gt; are both bidirectional and common, then bidirectional&lt;/li&gt;
&lt;li&gt;Otherwise, if &lt;code&gt;r&lt;/code&gt; and inner range are both forward, then forward&lt;/li&gt;
&lt;li&gt;Otherwise, input&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;common: when both &lt;code&gt;r&lt;/code&gt; and inner range are forward and common, and &lt;code&gt;r&lt;/code&gt; is a range of glvalue ranges&lt;/li&gt;
&lt;li&gt;sized: never&lt;/li&gt;
&lt;li&gt;const-iterable: when both &lt;code&gt;r&lt;/code&gt; and &lt;code&gt;p&lt;/code&gt; are const-iterable and &lt;code&gt;r&lt;/code&gt; is a range of glvalue ranges&lt;/li&gt;
&lt;li&gt;borrowed: never&lt;/li&gt;
&lt;li&gt;constant: when inner range and &lt;code&gt;p&lt;/code&gt; are both constant&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;code&gt;views::as_const(r: [T]) -&amp;gt; [T]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Produce a range with same element as in &lt;code&gt;r&lt;/code&gt;, but ensure that the result&apos;s element cannot be modified (i.e. a constant range).
(Basically, did a &lt;code&gt;std::as_const&lt;/code&gt; on each element so that you cannot modify them, albeit with a much more complicated algorithm that avoid wrapping if at all possible by delegate to &lt;code&gt;std::as_const&lt;/code&gt;)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;constraint: &lt;code&gt;r&lt;/code&gt; is an input range&lt;/li&gt;
&lt;li&gt;reference: &lt;code&gt;range_const_reference_t&amp;lt;R&amp;gt;&lt;/code&gt; (assuming &lt;code&gt;r&lt;/code&gt;&apos;s value type is just &lt;code&gt;remove_cvref_t&lt;/code&gt; of its reference, then for a range of &lt;code&gt;T&amp;amp;&lt;/code&gt;, just &lt;code&gt;const T&amp;amp;&lt;/code&gt;; for a range of &lt;code&gt;T&amp;amp;&amp;amp;&lt;/code&gt;, just &lt;code&gt;const T&amp;amp;&amp;amp;&lt;/code&gt;; for a range of prvalue &lt;code&gt;T&lt;/code&gt;, just &lt;code&gt;T&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;value type: same as &lt;code&gt;r&lt;/code&gt;&apos;s value type&lt;/li&gt;
&lt;li&gt;category: same as &lt;code&gt;r&lt;/code&gt;&apos;s category (preserve contiguous)&lt;/li&gt;
&lt;li&gt;common: when &lt;code&gt;r&lt;/code&gt; is common&lt;/li&gt;
&lt;li&gt;sized: when &lt;code&gt;r&lt;/code&gt; is sized&lt;/li&gt;
&lt;li&gt;const-iterable: when &lt;code&gt;r&lt;/code&gt; is const-iterable&lt;/li&gt;
&lt;li&gt;borrowed: when &lt;code&gt;r&lt;/code&gt; is borrowed&lt;/li&gt;
&lt;li&gt;constant: always&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;code&gt;views::enumerate(r: [T]) -&amp;gt; [(N, T)]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Produce a range such that each of the original elements of &lt;code&gt;r&lt;/code&gt; is accompanied by its index in &lt;code&gt;r&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; enumerate([1, 3, 6])
[(0, 1), (1, 3), (2, 6)]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(Notice that the index type &lt;code&gt;N&lt;/code&gt; is &lt;code&gt;range_difference_t&amp;lt;R&amp;gt;&lt;/code&gt;)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;constraint: &lt;code&gt;r&lt;/code&gt; is an input range, and &lt;code&gt;T&lt;/code&gt; is move constructible.&lt;/li&gt;
&lt;li&gt;reference: &lt;code&gt;tuple&amp;lt;N, T&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;value type: &lt;code&gt;tuple&amp;lt;N, range_value_t&amp;lt;R&amp;gt;&amp;gt;&lt;/code&gt; (&lt;strong&gt;not&lt;/strong&gt; the reference type minus reference)&lt;/li&gt;
&lt;li&gt;category: at most random access&lt;/li&gt;
&lt;li&gt;common: when &lt;code&gt;r&lt;/code&gt; is common and sized&lt;/li&gt;
&lt;li&gt;sized: when &lt;code&gt;r&lt;/code&gt; is sized&lt;/li&gt;
&lt;li&gt;const-iterable: when &lt;code&gt;r&lt;/code&gt; is const-iterable&lt;/li&gt;
&lt;li&gt;borrowed: when &lt;code&gt;r&lt;/code&gt; is borrowed&lt;/li&gt;
&lt;li&gt;constant: when &lt;code&gt;r&lt;/code&gt; is constant&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;code&gt;views::adjacent&amp;lt;N: size_t&amp;gt;(r: [T]) -&amp;gt; [(T, T, ...)]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Produce a new range where each elements is a tuple of the next consecutive &lt;code&gt;N&lt;/code&gt; elements. &lt;code&gt;pairwise&lt;/code&gt; is an alias for &lt;code&gt;adjacent&amp;lt;2&amp;gt;&lt;/code&gt;.
If &lt;code&gt;r&lt;/code&gt; has less than &lt;code&gt;N&lt;/code&gt; elements, the resulting range is empty.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; adjacent&amp;lt;4&amp;gt;([1, 2, 3, 4, 5, 6])
[(1, 2, 3, 4), (2, 3, 4, 5), (3, 4, 5, 6)]
&amp;gt;&amp;gt;&amp;gt; pairwise([&quot;A&quot;, &quot;B&quot;, &quot;C&quot;])  # or adjacent&amp;lt;2&amp;gt;
[(&quot;A&quot;, &quot;B&quot;), (&quot;B&quot;, &quot;C&quot;)]
&amp;gt;&amp;gt;&amp;gt; adjacent&amp;lt;7&amp;gt;([1, 2, 3])
[]  # empty view with type tuple&amp;lt;int&amp;amp;, int&amp;amp;, ...&amp;gt; (repeat 7 times)
&amp;gt;&amp;gt;&amp;gt; adjacent&amp;lt;0&amp;gt;([1, 2, 3, 4, 5, 6])
[]  # empty view with type tuple&amp;lt;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;constraint: &lt;code&gt;r&lt;/code&gt; is a forward range and &lt;code&gt;N &amp;gt; 0&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;reference: &lt;code&gt;tuple&amp;lt;T, T, ...&amp;gt;&lt;/code&gt; (repeat &lt;code&gt;N&lt;/code&gt; times, note that &lt;code&gt;T&lt;/code&gt; is the reference type of &lt;code&gt;r&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;value type: &lt;code&gt;tuple&amp;lt;range_value_t&amp;lt;R&amp;gt;, ...&amp;gt;&lt;/code&gt; (repeat &lt;code&gt;N&lt;/code&gt; times, &lt;strong&gt;not&lt;/strong&gt; the reference type minus reference)&lt;/li&gt;
&lt;li&gt;category: at most random access&lt;/li&gt;
&lt;li&gt;common: when &lt;code&gt;r&lt;/code&gt; is common&lt;/li&gt;
&lt;li&gt;sized: when &lt;code&gt;r&lt;/code&gt; is sized&lt;/li&gt;
&lt;li&gt;const-iterable: when &lt;code&gt;r&lt;/code&gt; is const-iterable&lt;/li&gt;
&lt;li&gt;borrowed: when &lt;code&gt;r&lt;/code&gt; is borrowed&lt;/li&gt;
&lt;li&gt;constant: when &lt;code&gt;r&lt;/code&gt; is constant&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;code&gt;views::adjacent_transform&amp;lt;N: size_t&amp;gt;(f: (T, T, ...) -&amp;gt; U, r: [T]) -&amp;gt; [U]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Produce a new range where each elements is the result of &lt;code&gt;f(e1, e2, ...)&lt;/code&gt;, where &lt;code&gt;e1, e2, ...&lt;/code&gt; are the next consecutive &lt;code&gt;N&lt;/code&gt; elements. &lt;code&gt;pairwise_transform&lt;/code&gt; is an alias for &lt;code&gt;adjacent_transform&amp;lt;2&amp;gt;&lt;/code&gt;.
If &lt;code&gt;r&lt;/code&gt; has less than &lt;code&gt;N&lt;/code&gt; elements, the resulting range is empty.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; adjacent_transform&amp;lt;4&amp;gt;(f, [1, 2, 3, 4, 5, 6])
[f(1, 2, 3, 4), f(2, 3, 4, 5), f(3, 4, 5, 6)]
&amp;gt;&amp;gt;&amp;gt; adjacent_transform&amp;lt;4&amp;gt;((a, b, c, d) =&amp;gt; a + b + c + d, [1, 2, 3, 4, 5, 6])
[10, 14, 18]
&amp;gt;&amp;gt;&amp;gt; pairwise_transform((a, b) =&amp;gt; a + b, [&quot;A&quot;, &quot;B&quot;, &quot;C&quot;])  # or adjacent_transform&amp;lt;2&amp;gt;
[&quot;AB&quot;, &quot;BC&quot;]
&amp;gt;&amp;gt;&amp;gt; adjacent_transform&amp;lt;0&amp;gt;(f, [1, 2, 3, 4, 5, 6])
[]  # empty view with type of the result of f()
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;constraint: &lt;code&gt;r&lt;/code&gt; is a forward range, &lt;code&gt;N &amp;gt; 0&lt;/code&gt;, &lt;code&gt;F&lt;/code&gt; is move constructible and is an object type, and invocable by &lt;code&gt;T, T, ...&lt;/code&gt; (repeat &lt;code&gt;N&lt;/code&gt; times)&lt;/li&gt;
&lt;li&gt;reference: &lt;code&gt;U&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;value type: &lt;code&gt;remove_cvref_t&amp;lt;U&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;category: if &lt;code&gt;f(e1, e2, ...)&lt;/code&gt; does not return a lvalue reference, then input; otherwise at most random access&lt;/li&gt;
&lt;li&gt;common: when &lt;code&gt;r&lt;/code&gt; is common&lt;/li&gt;
&lt;li&gt;sized: when &lt;code&gt;r&lt;/code&gt; is sized&lt;/li&gt;
&lt;li&gt;const-iterable: when &lt;code&gt;r&lt;/code&gt; is const-iterable and &lt;code&gt;f&lt;/code&gt; is const-invocable&lt;/li&gt;
&lt;li&gt;borrowed: never&lt;/li&gt;
&lt;li&gt;constant: when the result of &lt;code&gt;f(e1, e2, ...)&lt;/code&gt; is a non-class or &lt;code&gt;std::tuple&lt;/code&gt; (pr)value, or when it returns a constant reference&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;code&gt;views::chunk(r: [T], n: N) -&amp;gt; [[T]]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Produce a new range-of-range that is the result of dividing &lt;code&gt;r&lt;/code&gt; into non-overlapping &lt;code&gt;n&lt;/code&gt;-sized chunks (except that last chunk can be smaller than &lt;code&gt;n&lt;/code&gt;).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; chunk([1, 2, 3, 4, 5], 2)
[[1, 2], [3, 4], [5]]
&amp;gt;&amp;gt;&amp;gt; chunk([1, 2, 3, 4], 8)
[[1, 2, 3, 4]]
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;constraint: &lt;code&gt;n &amp;gt; 0&lt;/code&gt; and &lt;code&gt;N&lt;/code&gt; is convertible to &lt;code&gt;r&lt;/code&gt;&apos;s difference type&lt;/li&gt;
&lt;li&gt;reference: &lt;code&gt;[T]&lt;/code&gt; (if &lt;code&gt;r&lt;/code&gt; is forward, then actually the reference type is &lt;code&gt;ranges::subrange&amp;lt;iterator_t&amp;lt;R&amp;gt;&amp;gt;&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;value type: same as reference type (inner range&apos;s value type same as &lt;code&gt;r&lt;/code&gt;&apos;s)&lt;/li&gt;
&lt;li&gt;category: outer range at most random access, inner range same as &lt;code&gt;r&lt;/code&gt; (preserve contiguous)&lt;/li&gt;
&lt;li&gt;common: outer range when &lt;code&gt;r&lt;/code&gt; is common and either both sized and bidirectional, or forward and not bidirectional; inner range when &lt;code&gt;r&lt;/code&gt; is common&lt;/li&gt;
&lt;li&gt;sized: outer range when &lt;code&gt;r&lt;/code&gt; is sized, inner range when &lt;code&gt;r&lt;/code&gt;&apos;s sentinel is a sized sentinel (common case is when &lt;code&gt;r&lt;/code&gt; is random access)&lt;/li&gt;
&lt;li&gt;const-iterable: (both outer and inner range) when &lt;code&gt;r&lt;/code&gt; is const-iterable and forward&lt;/li&gt;
&lt;li&gt;borrowed: outer range when &lt;code&gt;r&lt;/code&gt; is borrowed and forward, inner range when &lt;code&gt;r&lt;/code&gt; is forward&lt;/li&gt;
&lt;li&gt;constant: outer range never, inner range when &lt;code&gt;r&lt;/code&gt; is constant&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;code&gt;views::slide(r: [T], n: N) -&amp;gt; [[T]]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Produce a new range-of-range that is the result of dividing &lt;code&gt;r&lt;/code&gt; into overlapping &lt;code&gt;n&lt;/code&gt;-sized chunks (basically, the &lt;code&gt;m&lt;/code&gt;-th range is a view into the &lt;code&gt;m&lt;/code&gt;-th through &lt;code&gt;m+n-1&lt;/code&gt;-th elements of &lt;code&gt;r&lt;/code&gt;).
This is similar to &lt;code&gt;views::adjacent&amp;lt;n&amp;gt;&lt;/code&gt; with the difference being that &lt;code&gt;adjacent&lt;/code&gt; require a compile-time size and produce range-of-tuples, while &lt;code&gt;views::slide&lt;/code&gt; require a runtime size
and provide range-of-ranges.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; slide([1, 2, 3, 4, 5], 2)
[[1, 2], [2, 3], [3, 4], [4, 5]]
&amp;gt;&amp;gt;&amp;gt; slide([1, 2, 3, 4], 8)
[]
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;constraint: &lt;code&gt;r&lt;/code&gt; is a forward range, &lt;code&gt;n &amp;gt; 0&lt;/code&gt;, and &lt;code&gt;N&lt;/code&gt; is convertible to &lt;code&gt;r&lt;/code&gt;&apos;s difference type&lt;/li&gt;
&lt;li&gt;reference: &lt;code&gt;[T]&lt;/code&gt; (specifically, the reference type is precisely the result type of &lt;code&gt;views::counted(r.begin(), n)&lt;/code&gt;, which is a span (when &lt;code&gt;r&lt;/code&gt; is contiguous) or a &lt;code&gt;ranges::subrange&lt;/code&gt; otherwise)&lt;/li&gt;
&lt;li&gt;value type: same as reference type (inner range&apos;s value type same as &lt;code&gt;r&lt;/code&gt;&apos;s)&lt;/li&gt;
&lt;li&gt;category: outer range at most random access, inner range same as &lt;code&gt;r&lt;/code&gt; (preserve contiguous)&lt;/li&gt;
&lt;li&gt;common: outer range when &lt;code&gt;r&lt;/code&gt; is common or is both random access and sized, inner range when &lt;code&gt;r&lt;/code&gt; is random access&lt;/li&gt;
&lt;li&gt;sized: outer range when &lt;code&gt;r&lt;/code&gt; is sized, inner range always&lt;/li&gt;
&lt;li&gt;const-iterable: outer range when &lt;code&gt;r&lt;/code&gt; is const-iterable and random access and sized, inner range always&lt;/li&gt;
&lt;li&gt;borrowed: outer range when &lt;code&gt;r&lt;/code&gt; is borrowed, inner range always&lt;/li&gt;
&lt;li&gt;constant: outer range never, inner range when &lt;code&gt;r&lt;/code&gt; is constant&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;code&gt;views::chunk_by(r: [T], f: (T, T) -&amp;gt; bool) -&amp;gt; [[T]]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Produce a new range-of-range such that &lt;code&gt;f&lt;/code&gt; is invoked on consecutive elements, and a new group is started when &lt;code&gt;f&lt;/code&gt; returns &lt;code&gt;false&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; chunk_by([1, 2, 2, 3, 1, 2, 0, 4, 5, 2], (a, b) =&amp;gt; a &amp;lt;= b)
[[1, 2, 2, 3], [1, 2], [0, 4, 5], [2]]
&amp;gt;&amp;gt;&amp;gt; chunk_by([1, 2, 2, 3, 1, 2, 0, 4, 5, 2], (a, b) =&amp;gt; a &amp;gt;= b)
[[1], [2, 2], [3, 1], [2, 0], [4], [5, 2]]
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;constraint: &lt;code&gt;r&lt;/code&gt; is a forward range, and &lt;code&gt;f&lt;/code&gt; is invocable on any combination of &lt;code&gt;T&lt;/code&gt; and &lt;code&gt;r&lt;/code&gt;&apos;s value type (and return a contextually-convertible-to-&lt;code&gt;bool&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;reference: &lt;code&gt;[T]&lt;/code&gt; (specifically, the reference type is precisely &lt;code&gt;ranges::subrange&amp;lt;iterator_t&amp;lt;R&amp;gt;&amp;gt;&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;value type: same as reference type (inner range&apos;s value type same as &lt;code&gt;r&lt;/code&gt;&apos;s)&lt;/li&gt;
&lt;li&gt;category: outer range at most bidirectional, inner range same as &lt;code&gt;r&lt;/code&gt; (preserve contiguous)&lt;/li&gt;
&lt;li&gt;common: outer range when &lt;code&gt;r&lt;/code&gt; is common, inner range always&lt;/li&gt;
&lt;li&gt;sized: outer range never, inner range when &lt;code&gt;r&lt;/code&gt;&apos;s sentinel is a sized sentinel (common case is when &lt;code&gt;r&lt;/code&gt; is random access)&lt;/li&gt;
&lt;li&gt;const-iterable: outer range never, inner range when &lt;code&gt;r&lt;/code&gt; is const-iterable&lt;/li&gt;
&lt;li&gt;borrowed: outer range never, inner range always&lt;/li&gt;
&lt;li&gt;constant: outer range never, inner range when &lt;code&gt;r&lt;/code&gt; is constant&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;code&gt;views::stride(r: [T], n: N) -&amp;gt; [T]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Produce a new range consists of an evenly-spaced subset of &lt;code&gt;r&lt;/code&gt; (with space fixed at &lt;code&gt;n&lt;/code&gt;).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; stride([1, 2, 3, 4], 2)
[1, 3]
&amp;gt;&amp;gt;&amp;gt; stride([1, 2, 3, 4, 5, 6, 7], 3)
[1, 4, 7]
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;constraint: &lt;code&gt;n &amp;gt; 0&lt;/code&gt; and &lt;code&gt;N&lt;/code&gt; is convertible to &lt;code&gt;r&lt;/code&gt;&apos;s difference type&lt;/li&gt;
&lt;li&gt;reference: &lt;code&gt;T&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;value type: same as &lt;code&gt;r&lt;/code&gt;&apos;s value type&lt;/li&gt;
&lt;li&gt;category: at most random access&lt;/li&gt;
&lt;li&gt;common: when &lt;code&gt;r&lt;/code&gt; is common and either sized or non-bidirectional&lt;/li&gt;
&lt;li&gt;sized: when &lt;code&gt;r&lt;/code&gt; is sized&lt;/li&gt;
&lt;li&gt;const-iterable: when &lt;code&gt;r&lt;/code&gt; is const-iterable&lt;/li&gt;
&lt;li&gt;borrowed: when &lt;code&gt;r&lt;/code&gt; is borrowed&lt;/li&gt;
&lt;li&gt;constant: when &lt;code&gt;r&lt;/code&gt; is constant&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Other Standard Views&lt;/h2&gt;
&lt;h3&gt;&lt;code&gt;std::generator&amp;lt;T[, U[, Alloc]]&amp;gt; : [U ? T : T&amp;amp;&amp;amp;]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Produce a view of all the things you have &lt;code&gt;co_yield&lt;/code&gt;ed in a coroutine.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;std::generator&amp;lt;int&amp;gt; ints(int start = 0) {
    while (true) co_yield start++;
}

void f() {
    std::println(&quot;{}&quot;, ints(3) | views::take(3)); // [3, 4, 5]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;constraint: if &lt;code&gt;U&lt;/code&gt; is present, it is a cv-unqualified object type and &lt;code&gt;T&lt;/code&gt; is either a true reference or copy constructible&lt;/li&gt;
&lt;li&gt;reference: if &lt;code&gt;U&lt;/code&gt; is present, &lt;code&gt;T&lt;/code&gt;; otherwise &lt;code&gt;T&amp;amp;&amp;amp;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;value type: if &lt;code&gt;U&lt;/code&gt; is present, &lt;code&gt;U&lt;/code&gt;; otherwise &lt;code&gt;remove_cvref_t&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;category: input&lt;/li&gt;
&lt;li&gt;common: never&lt;/li&gt;
&lt;li&gt;sized: never&lt;/li&gt;
&lt;li&gt;const-iterable: never&lt;/li&gt;
&lt;li&gt;borrowed: never&lt;/li&gt;
&lt;li&gt;constant: when &lt;code&gt;T&lt;/code&gt; is a const reference&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;C++26 Range Adaptors&lt;/h1&gt;
&lt;h2&gt;Factories&lt;/h2&gt;
&lt;h3&gt;&lt;code&gt;views::concat(r1: [T1], r2: [T2], ...) -&amp;gt; [common_reference_t&amp;lt;T1, T2, ...&amp;gt;]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Produce a new range that is &lt;code&gt;r1&lt;/code&gt;, &lt;code&gt;r2&lt;/code&gt;, ... concated head-to-tail together; i.e. a range that starts at the first element of the first range, ends at the last element of the last range, with all
range elements sequenced in between respectively in the order of arguments.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; concat([1, 2, 3], [4, 5, 6])
[1, 2, 3, 4, 5, 6]
&amp;gt;&amp;gt;&amp;gt; concat([1, 2], [3, 4], [1.0, 2.0])
[1.0, 2.0, 3.0, 4.0, 1.0, 2.0]
# concat() is ill-formed
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;constraint: all of &lt;code&gt;T1&lt;/code&gt;, &lt;code&gt;T2&lt;/code&gt;, ... have a &lt;code&gt;common_reference_t&lt;/code&gt;, each of which is converted to that common reference type, and all of &lt;code&gt;r1&lt;/code&gt;, &lt;code&gt;r2&lt;/code&gt;, ...&apos;s value type have a &lt;code&gt;common_type_t&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;reference: &lt;code&gt;common_reference_t&amp;lt;T1, T2, ...&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;value type: &lt;code&gt;common_type_t&amp;lt;range_value_t&amp;lt;R1&amp;gt;, ...&amp;gt;&lt;/code&gt; (&lt;strong&gt;not&lt;/strong&gt; the reference type minus reference)&lt;/li&gt;
&lt;li&gt;category:
&lt;ul&gt;
&lt;li&gt;if all of &lt;code&gt;r1&lt;/code&gt;, &lt;code&gt;r2&lt;/code&gt;, ... are random access, and all but the last range are common, then random access&lt;/li&gt;
&lt;li&gt;otherwise, if all of &lt;code&gt;r1&lt;/code&gt;, &lt;code&gt;r2&lt;/code&gt;, ... are bidirectional, and all but the last range are common, then bidirectional&lt;/li&gt;
&lt;li&gt;otherwise, if all of &lt;code&gt;r1&lt;/code&gt;, &lt;code&gt;r2&lt;/code&gt;, ... are forward, then forward&lt;/li&gt;
&lt;li&gt;otherwise, input&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;common: when the last range &lt;code&gt;rn&lt;/code&gt; is common&lt;/li&gt;
&lt;li&gt;sized: when all of &lt;code&gt;r1&lt;/code&gt;, &lt;code&gt;r2&lt;/code&gt;, ... are sized&lt;/li&gt;
&lt;li&gt;const-iterable: when all of &lt;code&gt;r1&lt;/code&gt;, &lt;code&gt;r2&lt;/code&gt;, ... are const-iterable&lt;/li&gt;
&lt;li&gt;borrowed: never (can be made conditionally borrowed, but the space cost is too high)&lt;/li&gt;
&lt;li&gt;constant: when all of &lt;code&gt;r1&lt;/code&gt;, &lt;code&gt;r2&lt;/code&gt;, ... are constant&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;code&gt;views::indices(n: N) -&amp;gt; [N]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;A convenient alias/alternative for &lt;code&gt;views::iota(0uz, ranges::size(r))&lt;/code&gt;. Basically, produce a range of &lt;code&gt;0&lt;/code&gt;, &lt;code&gt;1&lt;/code&gt;, ..., &lt;code&gt;n - 1&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; indices(5)
[0, 1, 2, 3, 4]
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;constraint: &lt;code&gt;N&lt;/code&gt; must be an integral type&lt;/li&gt;
&lt;li&gt;reference: &lt;code&gt;N&lt;/code&gt; (prvalue range!)&lt;/li&gt;
&lt;li&gt;value type: &lt;code&gt;N&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;category: random access&lt;/li&gt;
&lt;li&gt;common: always&lt;/li&gt;
&lt;li&gt;sized: always&lt;/li&gt;
&lt;li&gt;const-iterable: always&lt;/li&gt;
&lt;li&gt;borrowed: always (iterator owns the current value)&lt;/li&gt;
&lt;li&gt;constant: always&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Real Adaptors&lt;/h2&gt;
&lt;h3&gt;&lt;code&gt;views::cache_latest(r: [T]) -&amp;gt; [T&amp;amp;]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Cache the last element of any range to avoid extra work.
For example: &lt;code&gt;r | views::transform(f) | views::filter(g)&lt;/code&gt; will call &lt;code&gt;f&lt;/code&gt; twice for every element of &lt;code&gt;r&lt;/code&gt; when iterating, because &lt;code&gt;filter&lt;/code&gt; dereferences twice on each iteration. If you add &lt;code&gt;views::cache_latest&lt;/code&gt; between the two adaptor, &lt;code&gt;f&lt;/code&gt; will only be called once per element.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;constraint: &lt;code&gt;r&lt;/code&gt; is an input range&lt;/li&gt;
&lt;li&gt;reference: &lt;code&gt;T&amp;amp;&lt;/code&gt; (force lvalue reference here)&lt;/li&gt;
&lt;li&gt;value type: same as &lt;code&gt;r&lt;/code&gt;&apos;s value type&lt;/li&gt;
&lt;li&gt;category: input&lt;/li&gt;
&lt;li&gt;common: never&lt;/li&gt;
&lt;li&gt;sized: when &lt;code&gt;r&lt;/code&gt; is sized&lt;/li&gt;
&lt;li&gt;const-iterable: never&lt;/li&gt;
&lt;li&gt;borrowed: never&lt;/li&gt;
&lt;li&gt;constant: when &lt;code&gt;r&lt;/code&gt; is constant&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;code&gt;views::as_input(r: [T]) -&amp;gt; [T]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Downgrade any range to an input, non-common range.&lt;/p&gt;
&lt;p&gt;Useful to avoid expensive operations that many range algorithm/adaptor perform to preserve higher properties. For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;views::join&lt;/code&gt;&apos;s iterator comparison need to do two base iterator comparisons (one for outer and one for inner) for common range, but only one is needed for non-common range.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;views::chunk&lt;/code&gt; have more expensive algorithm when passed with a forward range: iterating through chunk border will incur a whole pass of all the elements for forward ranges.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;[!NOTE]
&lt;code&gt;views::as_input&lt;/code&gt; will produce &lt;code&gt;r&lt;/code&gt;&apos;s type whenever possible&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;constraint: &lt;code&gt;r&lt;/code&gt; is an input range&lt;/li&gt;
&lt;li&gt;reference: &lt;code&gt;T&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;value type: same as &lt;code&gt;r&lt;/code&gt;&apos;s value type&lt;/li&gt;
&lt;li&gt;category: input (this is the point of this adaptor)&lt;/li&gt;
&lt;li&gt;common: never&lt;/li&gt;
&lt;li&gt;sized: when &lt;code&gt;r&lt;/code&gt; is sized&lt;/li&gt;
&lt;li&gt;const-iterable: when &lt;code&gt;r&lt;/code&gt; is const-iterable&lt;/li&gt;
&lt;li&gt;borrowed: when &lt;code&gt;r&lt;/code&gt; is borrowed&lt;/li&gt;
&lt;li&gt;constant: when &lt;code&gt;r&lt;/code&gt; is constant&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Other Standard Views&lt;/h2&gt;
&lt;h3&gt;&lt;code&gt;std::optional&amp;lt;T&amp;gt;: [T&amp;amp;]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;In C++26, &lt;code&gt;std::optional&amp;lt;T&amp;gt;&lt;/code&gt;, who represents an object that may or may not store a &lt;code&gt;T&lt;/code&gt;, is upgraded to model &lt;code&gt;view&lt;/code&gt;. The underlying intention is for &lt;code&gt;optional&lt;/code&gt; to behave as a container of 0 or 1 elements.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; optional&amp;lt;int&amp;gt;()
[]
&amp;gt;&amp;gt;&amp;gt; optional&amp;lt;int&amp;gt;(1)
[1]
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;reference: &lt;code&gt;T&amp;amp;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;value type: &lt;code&gt;remove_cv_t&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;category: contiguous&lt;/li&gt;
&lt;li&gt;common: always&lt;/li&gt;
&lt;li&gt;sized: always (0 if disengaged, 1 if engaged)&lt;/li&gt;
&lt;li&gt;const-iterable: always&lt;/li&gt;
&lt;li&gt;borrowed: never&lt;/li&gt;
&lt;li&gt;constant: when &lt;code&gt;T&lt;/code&gt; is &lt;code&gt;const&lt;/code&gt;-qualified&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Future Range Adaptors In Review&lt;/h1&gt;
&lt;h2&gt;Factories&lt;/h2&gt;
&lt;h3&gt;&lt;code&gt;views::nullable(n: std::maybe&amp;lt;T&amp;gt;) -&amp;gt; [T&amp;amp;]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;(Current design as of &lt;a href=&quot;https://wg21.link/P1255R12&quot;&gt;P1255R14&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;Produce a new range of 0 or 1 element based on a nullable object.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int* p = new int(3);
nullable(p) // [3]
int* q = nullptr;
nullable(q) // []
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;constraint: &lt;code&gt;N&lt;/code&gt; is copyable, an object type, and models &lt;code&gt;std::maybe&lt;/code&gt; (basically dereferencable and contextually convertible to &lt;code&gt;bool&lt;/code&gt;). (Or a &lt;code&gt;reference_wrapper&lt;/code&gt; of such a type)&lt;/li&gt;
&lt;li&gt;reference: &lt;code&gt;T&amp;amp;&lt;/code&gt; (the iterator type is actually &lt;code&gt;T*&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;value type: &lt;code&gt;T&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;category: contiguous&lt;/li&gt;
&lt;li&gt;common: always&lt;/li&gt;
&lt;li&gt;sized: always&lt;/li&gt;
&lt;li&gt;const-iterable: always&lt;/li&gt;
&lt;li&gt;borrowed: when &lt;code&gt;N&lt;/code&gt; is a pointer, a &lt;code&gt;reference_wrapper&lt;/code&gt; or a reference&lt;/li&gt;
&lt;li&gt;constant: when &lt;code&gt;T&lt;/code&gt; is &lt;code&gt;const&lt;/code&gt;-qualified&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;code&gt;any_view&amp;lt;V[, Opts[, R[, RR[, Diff]]]]&amp;gt;: [R ? R : V&amp;amp;]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;(Current design as of &lt;a href=&quot;https://wg21.link/P3411R6&quot;&gt;P3411R6&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;A type-erased view that allows customizing the traversal category and other properties. Useful for hiding the concrete result type of a range pipeline, such as:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ranges::any_view&amp;lt;Widget&amp;gt; getWidgets()
{
    std::vector&amp;lt;Widget&amp;gt; widgets_{ /* ... */ };
    return widgets_ | views::filter(/* ... */) | views::take_while(/* ... */);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, if you used &lt;code&gt;auto&lt;/code&gt; as return type, the return type will be &lt;code&gt;take_while_view&amp;lt;filter_view&amp;lt;vector&amp;lt;Widget&amp;gt;, ...&amp;gt;, ...&amp;gt;&lt;/code&gt;, which is not only complicated to spell and mangle, but also exposed internal implementation. Using &lt;code&gt;any_view&lt;/code&gt; here hide that nicely.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;Opts&lt;/code&gt; template parameter (defaults to &lt;code&gt;any_view_options::input&lt;/code&gt;) is a scoped enum that specifies the category, sized, borrowedness and copyability of the resulting &lt;code&gt;any_view&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;enum class any_view_options
{
    input = 1,
    forward = 3,
    bidirectional = 7,
    random_access = 15,
    contiguous = 31,
    approximately_sized = 32,
    sized = 96,
    borrowed = 128,
    copyable = 256
} Opts;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Users are expected to bit-or these options to construct the desired composition of properties. &lt;code&gt;RRef&lt;/code&gt; specifies the desired &lt;code&gt;range_rvalue_reference_t&lt;/code&gt; (defaults to &lt;code&gt;Ref - &amp;amp; + &amp;amp;&amp;amp;&lt;/code&gt;).&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;reference: &lt;code&gt;R&lt;/code&gt; (defaults to &lt;code&gt;V&amp;amp;&lt;/code&gt; if not specified)&lt;/li&gt;
&lt;li&gt;value type: &lt;code&gt;V&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;category: depends on &lt;code&gt;Opts&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;common: never&lt;/li&gt;
&lt;li&gt;sized: depends on &lt;code&gt;Opts&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;const-iterable: never&lt;/li&gt;
&lt;li&gt;borrowed: depends on &lt;code&gt;Opts&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;constant: depends on &lt;code&gt;V&lt;/code&gt;, &lt;code&gt;R&lt;/code&gt; and &lt;code&gt;RRef&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;code&gt;views::null_term(r: *[T]) -&amp;gt; [T]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;(Current design as of &lt;a href=&quot;https://wg21.link/P3705R2&quot;&gt;P3705R2&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;A view that produces a null-terminated range from an starting iterator. For instance, for &lt;code&gt;const char* long_string&lt;/code&gt;, &lt;code&gt;views::null_term(long_string)&lt;/code&gt; effectively represents a &lt;code&gt;cstring_view&lt;/code&gt; of this NTBS without the overhead of computing the length.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!TIP]
This view is just an alias of &lt;code&gt;subrange(r, std::null_sentinel)&lt;/code&gt;, where &lt;code&gt;null_sentinel&lt;/code&gt; is a simple sentinel providing &lt;code&gt;operator==&lt;/code&gt; that forwards to &lt;code&gt;*rng == T()&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;constraint: &lt;code&gt;r&lt;/code&gt; is an iterator with a default initializable value type.&lt;/li&gt;
&lt;li&gt;reference: &lt;code&gt;T&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;value type: same as &lt;code&gt;r&lt;/code&gt;&apos;s value type&lt;/li&gt;
&lt;li&gt;category: same as &lt;code&gt;r&lt;/code&gt; (preserve contiguous)&lt;/li&gt;
&lt;li&gt;common: never&lt;/li&gt;
&lt;li&gt;sized: never&lt;/li&gt;
&lt;li&gt;const-iterable: when &lt;code&gt;r&lt;/code&gt; is const-iterable&lt;/li&gt;
&lt;li&gt;borrowed: never&lt;/li&gt;
&lt;li&gt;constant: when &lt;code&gt;r&lt;/code&gt; is constant&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;code&gt;views::set_{difference,intersection,union,symmetric_difference}(r1: [T], r2: [U], ...) -&amp;gt; [T] | [U] | ...&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;(Current design as of &lt;a href=&quot;https://wg21.link/P3741R1&quot;&gt;P3741R1&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;These four views performs common set operations between two ranges:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;views::set_difference(A, B)&lt;/code&gt; returns elements in &lt;code&gt;A&lt;/code&gt; that is not in &lt;code&gt;B&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;views::set_intersection(A, B, ...)&lt;/code&gt; returns elements in &lt;code&gt;A&lt;/code&gt; that is also in &lt;code&gt;B&lt;/code&gt; (and all other ranges provided)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;views::set_union(A, B, ...)&lt;/code&gt; returns elements that are either in &lt;code&gt;A&lt;/code&gt; or in &lt;code&gt;B&lt;/code&gt; or both (or either of the other ranges provided)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;views::set_symmetric_difference(A, B)&lt;/code&gt; returns elements that are either in &lt;code&gt;A&lt;/code&gt; or in &lt;code&gt;B&lt;/code&gt;, but not both&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; set_difference([1, 2, 3, 4], [1, 2, 5, 6])
[3, 4]
&amp;gt;&amp;gt;&amp;gt; set_intersection([1, 2, 3, 4], [1, 2, 5, 6], [1, 2, 7, 8])
[1, 2]
&amp;gt;&amp;gt;&amp;gt; set_union([1, 2, 3, 4], [1, 2, 5, 6], [1, 2, 7, 8])
[1, 2, 3, 4, 5, 6, 7, 8]
&amp;gt;&amp;gt;&amp;gt; set_symmetric_difference([1, 2, 3, 4], [1, 2, 5, 6])
[3, 4, 5, 6]
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;constraint: &lt;code&gt;r1&lt;/code&gt; and &lt;code&gt;r2&lt;/code&gt; (and any additional ranges) are both input ranges; for every pair of provided ranges, their iterator have an indirect strict weak order (basically just have comparison between &lt;code&gt;T&lt;/code&gt; an &lt;code&gt;U&lt;/code&gt; and their value types). For &lt;code&gt;union&lt;/code&gt; and &lt;code&gt;symmetric_difference&lt;/code&gt;, also requires that all provided ranges can be concatenated using &lt;code&gt;views::concat&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;reference: for &lt;code&gt;difference&lt;/code&gt; and &lt;code&gt;intersection&lt;/code&gt;, &lt;code&gt;T&lt;/code&gt;; for &lt;code&gt;union&lt;/code&gt; and &lt;code&gt;symmetric_difference&lt;/code&gt;, &lt;code&gt;common_reference_t&amp;lt;T, U, ...&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;value type: for &lt;code&gt;difference&lt;/code&gt; and &lt;code&gt;intersection&lt;/code&gt;, same as &lt;code&gt;r1&lt;/code&gt;&apos;s value type; for &lt;code&gt;union&lt;/code&gt; and &lt;code&gt;symmetric_difference&lt;/code&gt;, &lt;code&gt;common_type_t&amp;lt;range_value_t&amp;lt;R1&amp;gt;, range_value_t&amp;lt;R2&amp;gt;&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;category: at most forward&lt;/li&gt;
&lt;li&gt;common: never&lt;/li&gt;
&lt;li&gt;sized: never&lt;/li&gt;
&lt;li&gt;const-iterable: for &lt;code&gt;set_union&lt;/code&gt;, always; for other three, never&lt;/li&gt;
&lt;li&gt;borrowed: never&lt;/li&gt;
&lt;li&gt;constant: when all provided ranges are constant&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Real Adaptors&lt;/h2&gt;
&lt;h3&gt;&lt;code&gt;views::flat_map(r: [T], f: T -&amp;gt; [U]) -&amp;gt; [U]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;(Current design as of &lt;a href=&quot;https://wg21.link/P3211R2&quot;&gt;P3211R2&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;Transform the input sequence to a range-of-range, and then join all the ranges. Following &lt;a href=&quot;https://wg21.link/P2328&quot;&gt;P2328&lt;/a&gt;, this adaptor can be implemented directly as &lt;code&gt;views::transform(r, f) | views::cache_latest | views::join&lt;/code&gt;, but this adaptor is not just an alias for that because a standalone view can cache the transform result and avoid repeated calls without reducing the entire range back to input (which &lt;code&gt;cache_latest&lt;/code&gt; would do).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; flat_map([0, 1, 2], x =&amp;gt; [x, x, x])
[0, 0, 0, 1, 1, 1, 2, 2, 2]
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;constraint: &lt;code&gt;r&lt;/code&gt; and &lt;code&gt;[U]&lt;/code&gt; are both input ranges, &lt;code&gt;F&lt;/code&gt; is move-constructible, an object type, and is invocable by &lt;code&gt;T&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;reference: &lt;code&gt;U&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;value type: same as &lt;code&gt;[U]&lt;/code&gt;&apos;s value type (the value type of invocation result)&lt;/li&gt;
&lt;li&gt;category:
&lt;ul&gt;
&lt;li&gt;If &lt;code&gt;[U]&lt;/code&gt; is a glvalue range, then at most bidirectional based on &lt;code&gt;[U]&lt;/code&gt;&apos;s category&lt;/li&gt;
&lt;li&gt;Otherwise (range of prvalue ranges), input&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;common: when both &lt;code&gt;r&lt;/code&gt; and &lt;code&gt;[U]&lt;/code&gt; are forward and common, and &lt;code&gt;[U]&lt;/code&gt; is a glvalue range&lt;/li&gt;
&lt;li&gt;sized: never&lt;/li&gt;
&lt;li&gt;const-iterable: when &lt;code&gt;r&lt;/code&gt; is const-iterable and &lt;code&gt;f&lt;/code&gt; is const-invocable, and &lt;code&gt;[U]&lt;/code&gt; is a glvalue range&lt;/li&gt;
&lt;li&gt;borrowed: when &lt;code&gt;r&lt;/code&gt; is borrowed and forward, &lt;code&gt;[U]&lt;/code&gt; is borrowed, and &lt;code&gt;F&lt;/code&gt; is tidy  (i.e. empty and trivially default constructible and trivially destructible)&lt;/li&gt;
&lt;li&gt;constant: when &lt;code&gt;[U]&lt;/code&gt; is constant&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;code&gt;views::slice(r: [T], m: N, n: N) -&amp;gt; [T]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;(Current design as of &lt;a href=&quot;https://wg21.link/P3216R3&quot;&gt;P3216R3&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;Produce a new range consists of the &lt;code&gt;m&lt;/code&gt;-th to &lt;code&gt;n&lt;/code&gt;-th (as usual, left inclusive, right exclusive) elements of &lt;code&gt;r&lt;/code&gt;. If &lt;code&gt;r&lt;/code&gt; has less than &lt;code&gt;n&lt;/code&gt; elements, contains all the elements after the &lt;code&gt;m&lt;/code&gt;-th. If &lt;code&gt;r&lt;/code&gt; has less than &lt;code&gt;m&lt;/code&gt; elements, produce an empty range.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; slice([1, 2, 3, 4, 5], 1, 3)
[2, 3]
&amp;gt;&amp;gt;&amp;gt; slice([1, 2, 3, 4, 5], 1, 10)
[2, 3, 4, 5]
&amp;gt;&amp;gt;&amp;gt; slice([1, 2, 3, 4, 5], 10, 12)
[]
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;[!NOTE]
&lt;code&gt;views::slice&lt;/code&gt; will produce &lt;code&gt;r&lt;/code&gt;&apos;s type whenever possible (for example, &lt;code&gt;empty_view&lt;/code&gt; passed in will return an &lt;code&gt;empty_view&lt;/code&gt;), even if &lt;code&gt;views::slice(r, m, n)&lt;/code&gt; is not just an alias for &lt;code&gt;views::take(views::drop(r, m), n - m)&lt;/code&gt;. (The reason for a dedicated view boils down to performance and support for &lt;code&gt;reserve_hint()&lt;/code&gt;.)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;constraint: &lt;code&gt;n &amp;gt;= m &amp;amp;&amp;amp; m &amp;gt;= 0&lt;/code&gt; and &lt;code&gt;N&lt;/code&gt; is convertible to &lt;code&gt;r&lt;/code&gt;&apos;s difference type&lt;/li&gt;
&lt;li&gt;reference: &lt;code&gt;T&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;value type: same as &lt;code&gt;r&lt;/code&gt;&apos;s value type&lt;/li&gt;
&lt;li&gt;category: same as &lt;code&gt;r&lt;/code&gt; (preserve contiguous)&lt;/li&gt;
&lt;li&gt;common: when &lt;code&gt;r&lt;/code&gt; is sized and random access&lt;/li&gt;
&lt;li&gt;sized: when &lt;code&gt;r&lt;/code&gt; is sized&lt;/li&gt;
&lt;li&gt;const-iterable: when &lt;code&gt;r&lt;/code&gt; is const-iterable or input and not forward&lt;/li&gt;
&lt;li&gt;borrowed: when &lt;code&gt;r&lt;/code&gt; is borrowed&lt;/li&gt;
&lt;li&gt;constant: when &lt;code&gt;r&lt;/code&gt; is constant&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;code&gt;views::unchecked_take(r: [T], n: N) -&amp;gt; [T]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;(Current design as of &lt;a href=&quot;https://wg21.link/P3230R3&quot;&gt;P3230R3&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;A variation of &lt;code&gt;views::take&lt;/code&gt; that assumes there are at least &lt;code&gt;n&lt;/code&gt; elements in &lt;code&gt;r&lt;/code&gt;.
In other words, more efficient in common cases but is UB if you try to take more than length elements.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; unchecked_take([1, 2, 3, 4], 2)
[1, 2]
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;[!NOTE]
Note that &lt;code&gt;views::unchecked_take&lt;/code&gt; will produce &lt;code&gt;r&lt;/code&gt;&apos;s type whenever possible (for example, &lt;code&gt;span&lt;/code&gt; passed in will return an &lt;code&gt;span&lt;/code&gt;). Also note that &lt;code&gt;views::unchecked_take&lt;/code&gt; may downgrade infinite ranges to finite ones (&lt;code&gt;views::iota(0) | views::unchecked_take(5)&lt;/code&gt; is just &lt;code&gt;views::iota(0, 5)&lt;/code&gt;, while &lt;code&gt;views::take&lt;/code&gt; cannot preserve type when &lt;code&gt;iota_view&lt;/code&gt; is not sized).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;constraint: &lt;code&gt;n &amp;gt;= 0 &amp;amp;&amp;amp; n &amp;lt;= ranges::distance(r)&lt;/code&gt; and &lt;code&gt;N&lt;/code&gt; is convertible to &lt;code&gt;r&lt;/code&gt;&apos;s difference type&lt;/li&gt;
&lt;li&gt;reference: &lt;code&gt;T&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;value type: same as &lt;code&gt;r&lt;/code&gt;&apos;s value type&lt;/li&gt;
&lt;li&gt;category: same as &lt;code&gt;r&lt;/code&gt; (preserve contiguous)&lt;/li&gt;
&lt;li&gt;common: when &lt;code&gt;r&lt;/code&gt; is random access&lt;/li&gt;
&lt;li&gt;sized: always (&lt;code&gt;n&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;const-iterable: when &lt;code&gt;r&lt;/code&gt; is const-iterable&lt;/li&gt;
&lt;li&gt;borrowed: when &lt;code&gt;r&lt;/code&gt; is borrowed&lt;/li&gt;
&lt;li&gt;constant: when &lt;code&gt;r&lt;/code&gt; is constant&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;code&gt;views::unchecked_drop(r: [T], n: N) -&amp;gt; [T]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;(Current design as of &lt;a href=&quot;https://wg21.link/P3230R3&quot;&gt;P3230R3&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;A variation of &lt;code&gt;views::drop&lt;/code&gt; that assumes there are at least &lt;code&gt;n&lt;/code&gt; elements in &lt;code&gt;r&lt;/code&gt;.
In other words, more efficient in common cases but is UB if you try to drop more than length elements.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; unchecked_drop([1, 2, 3, 4], 2)
[3, 4]
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;[!NOTE]
&lt;code&gt;views::unchecked_drop&lt;/code&gt; will produce &lt;code&gt;r&lt;/code&gt;&apos;s type whenever possible (for example, &lt;code&gt;span&lt;/code&gt; passed in will return an &lt;code&gt;span&lt;/code&gt;). Also note that &lt;code&gt;views::unchecked_drop&lt;/code&gt; may process infinite ranges better (&lt;code&gt;views::iota(0) | views::unchecked_drop(5)&lt;/code&gt; is just &lt;code&gt;views::iota(5)&lt;/code&gt;, while &lt;code&gt;views::drop&lt;/code&gt; cannot preserve type when &lt;code&gt;iota_view&lt;/code&gt; is not sized).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;constraint: &lt;code&gt;n &amp;gt;= 0 &amp;amp;&amp;amp; n &amp;lt;= ranges::distance(r)&lt;/code&gt; and &lt;code&gt;N&lt;/code&gt; is convertible to &lt;code&gt;r&lt;/code&gt;&apos;s difference type&lt;/li&gt;
&lt;li&gt;reference: &lt;code&gt;T&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;value type: same as &lt;code&gt;r&lt;/code&gt;&apos;s value type&lt;/li&gt;
&lt;li&gt;category: same as &lt;code&gt;r&lt;/code&gt; (preserve contiguous)&lt;/li&gt;
&lt;li&gt;common: when &lt;code&gt;r&lt;/code&gt; is common&lt;/li&gt;
&lt;li&gt;sized: when &lt;code&gt;r&lt;/code&gt; is sized&lt;/li&gt;
&lt;li&gt;const-iterable: when &lt;code&gt;r&lt;/code&gt; is const-iterable or input and not forward&lt;/li&gt;
&lt;li&gt;borrowed: when &lt;code&gt;r&lt;/code&gt; is borrowed&lt;/li&gt;
&lt;li&gt;constant: when &lt;code&gt;r&lt;/code&gt; is constant&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;code&gt;views::take_before(r: [T], p: U) -&amp;gt; [T]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;(Current design as of &lt;a href=&quot;https://wg21.link/P3220R3&quot;&gt;P3220R3&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;Produce a new range that includes all the element of &lt;code&gt;r&lt;/code&gt; until &lt;code&gt;p&lt;/code&gt; (inclusive). Similar to &lt;code&gt;views::take_while&lt;/code&gt; but using a value instead of a predicate for ending detection. Very useful in cases like importing NTBS ranges with &lt;code&gt;views::take_before(str, &apos;\0&apos;)&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; take_before([1, 2, 3, 4, 5], 3)
[1, 2, 3]
&amp;gt;&amp;gt;&amp;gt; take_before([1, 2, 3, 4, 5], 6)
[1, 2, 3, 4, 5]
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;constraint: &lt;code&gt;r&lt;/code&gt; is an input range, and &lt;code&gt;U&lt;/code&gt; is an object type and move constructible and &lt;code&gt;t == u&lt;/code&gt; is well-formed for both &lt;code&gt;T&lt;/code&gt; and &lt;code&gt;r&lt;/code&gt;&apos;s value type&lt;/li&gt;
&lt;li&gt;reference: &lt;code&gt;T&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;value type: same as &lt;code&gt;r&lt;/code&gt;&apos;s value type&lt;/li&gt;
&lt;li&gt;category: same as &lt;code&gt;r&lt;/code&gt; (preserve contiguous)&lt;/li&gt;
&lt;li&gt;common: never (&lt;code&gt;begin()&lt;/code&gt; must return an iterator-to-&lt;code&gt;r&lt;/code&gt;, so &lt;code&gt;end()&lt;/code&gt; cannot reuse that iterator type)&lt;/li&gt;
&lt;li&gt;sized: never&lt;/li&gt;
&lt;li&gt;const-iterable: when &lt;code&gt;r&lt;/code&gt; is const-iterable and &lt;code&gt;U&lt;/code&gt; is equality comparable with the reference and lvalue of value type of &lt;code&gt;as_const(r)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;borrowed: when &lt;code&gt;T&lt;/code&gt; is a scalar type&lt;/li&gt;
&lt;li&gt;constant: when &lt;code&gt;r&lt;/code&gt; is constant&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;code&gt;views::cycle(r: [T][, n: N]) -&amp;gt; [T]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;(Current design as of &lt;a href=&quot;https://wg21.link/P3806R1&quot;&gt;P3806R1&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;Produce a new range that repeatedly cycle through all the element of &lt;code&gt;r&lt;/code&gt;. The adaptor also supports passing in an optional count parameter to cycle for &lt;code&gt;n&lt;/code&gt; times instead of infinity.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; cycle([1, 2, 3])
[1, 2, 3, 1, 2, 3, 1, 2, 3, ...]
&amp;gt;&amp;gt;&amp;gt; cycle([1, 2, 3], 2)
[1, 2, 3, 1, 2, 3]
&amp;gt;&amp;gt;&amp;gt; cycle([])
[]
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;constraint: &lt;code&gt;r&lt;/code&gt; is a forward range; &lt;code&gt;N&lt;/code&gt; (if provided) is convertible to an implementation-defined signed-integer-like type&lt;/li&gt;
&lt;li&gt;reference: &lt;code&gt;T&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;value type: same as &lt;code&gt;r&lt;/code&gt;&apos;s value type&lt;/li&gt;
&lt;li&gt;category: if &lt;code&gt;r&lt;/code&gt; is random access and sized, then random access; otherwise, at most bidirectional&lt;/li&gt;
&lt;li&gt;common: when &lt;code&gt;n&lt;/code&gt; is provided&lt;/li&gt;
&lt;li&gt;sized: when &lt;code&gt;n&lt;/code&gt; is provided and &lt;code&gt;r&lt;/code&gt; is sized&lt;/li&gt;
&lt;li&gt;const-iterable: when &lt;code&gt;r&lt;/code&gt; is const-iterable and &lt;code&gt;const R&lt;/code&gt; is a forward range&lt;/li&gt;
&lt;li&gt;borrowed: never&lt;/li&gt;
&lt;li&gt;constant: when &lt;code&gt;r&lt;/code&gt; is constant&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;code&gt;views::scan(r: [T], f: (Acc/T | U, T) -&amp;gt; U[, init: Acc]) -&amp;gt; [U]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;(Current design as of &lt;a href=&quot;https://wg21.link/P3351R4&quot;&gt;P3351R4&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;Produce a new range that takes a range and a function that takes the current element and the current state as parameters. Basically, &lt;code&gt;views::transform&lt;/code&gt; with a stateful function. Optionally takes an initial seed to be used as the initial accumulator.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; scan([1, 2, 3, 4, 5], (a, b) =&amp;gt; a + b)
[1, 3, 6, 10, 15]
&amp;gt;&amp;gt;&amp;gt; scan([1, 2, 3, 4, 5], (a, b) =&amp;gt; a + b, 10)
[11, 13, 16, 20, 25]
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;constraint: &lt;code&gt;F&lt;/code&gt; is move-constructible, an object type, and is invocable by either &lt;code&gt;(Acc, T)&lt;/code&gt; (if provided initial seed) or &lt;code&gt;(T, T)&lt;/code&gt; (if not), while also invocable by &lt;code&gt;(U, T)&lt;/code&gt; where &lt;code&gt;U&lt;/code&gt; is the return type of &lt;code&gt;F&lt;/code&gt; invoking on these parameter types.&lt;/li&gt;
&lt;li&gt;reference: &lt;code&gt;U&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;value type: &lt;code&gt;remove_cvref_t&amp;lt;U&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;category: input (due to stashing iterator concerns it cannot be forward)&lt;/li&gt;
&lt;li&gt;common: never (iterator need to store accumulator)&lt;/li&gt;
&lt;li&gt;sized: when &lt;code&gt;r&lt;/code&gt; is sized&lt;/li&gt;
&lt;li&gt;const-iterable: when &lt;code&gt;r&lt;/code&gt; is const-iterable and &lt;code&gt;f&lt;/code&gt; is const-invocable&lt;/li&gt;
&lt;li&gt;borrowed: when &lt;code&gt;F&lt;/code&gt; is tidy (i.e. empty and trivially default constructible and trivially destructible)&lt;/li&gt;
&lt;li&gt;constant: when &lt;code&gt;U&lt;/code&gt; is a value of non-class type (like prvalue range of &lt;code&gt;int&lt;/code&gt;) or a const reference (l/rvalue both applies)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;code&gt;views::{from,to}_{little,big}_endian(r: [T]) -&amp;gt; [T]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;(Current design as of &lt;a href=&quot;https://wg21.link/P4030R1&quot;&gt;P4030R1&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;Produce a new range that converts the endian-ness of the input. If the native endian-ness if big endian, then &lt;code&gt;{from,to}_big_endian&lt;/code&gt; is a no-op; otherwise, &lt;code&gt;{from,to}_little_endian&lt;/code&gt; is a no-op. When these views are not no-ops, they are a wrapper around &lt;code&gt;views::transform&lt;/code&gt; with &lt;code&gt;std::byteswap&lt;/code&gt; as the transformer.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; to_little_endian([0x12345678])
[0x78563412]  # if the native endian is big
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;constraint: The value type of &lt;code&gt;r&lt;/code&gt; models &lt;code&gt;std::integral&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;reference: &lt;code&gt;T&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;value type: &lt;code&gt;remove_cvref_t&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;category: same as &lt;code&gt;r&lt;/code&gt; (preserve contiguous)&lt;/li&gt;
&lt;li&gt;common: when &lt;code&gt;r&lt;/code&gt; is common&lt;/li&gt;
&lt;li&gt;sized: when &lt;code&gt;r&lt;/code&gt; is sized&lt;/li&gt;
&lt;li&gt;const-iterable: when &lt;code&gt;r&lt;/code&gt; is const-iterable&lt;/li&gt;
&lt;li&gt;borrowed: never&lt;/li&gt;
&lt;li&gt;constant: always&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;code&gt;views::to_utf{8,16,32}[_or_error](r: [T]) -&amp;gt; [Type | expected&amp;lt;Type, utf_transcoding_error&amp;gt;]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;(Current design as of &lt;a href=&quot;https://wg21.link/P2728R14&quot;&gt;P2728R14&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;Produce a new range that represents the conversion result from &lt;code&gt;r&lt;/code&gt; to the specified UTF format. Also provides &lt;code&gt;_or_error&lt;/code&gt; variant that returns &lt;code&gt;expected&amp;lt;Type, utf_transcoding_error&amp;gt;&lt;/code&gt; to represent the specific error encountered.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; to_utf32(u8&quot;🙂&quot;)
U&quot;🙂&quot;
&amp;gt;&amp;gt;&amp;gt; to_utf32(u8&quot;🙂&quot; | take(3))
U&quot;�&quot;  # Replacement character when facing invalid code units
&amp;gt;&amp;gt;&amp;gt; to_utf32_or_error(u8&quot;🙂&quot; | take(3))
[unexpected{truncated_utf8_sequence}]
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;constraint: &lt;code&gt;T&lt;/code&gt; is one of &lt;code&gt;cv char{8,16,32}_t&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;reference: &lt;code&gt;charN_t&lt;/code&gt; for &lt;code&gt;to_utfN&lt;/code&gt;, &lt;code&gt;expected&amp;lt;charN_t, utf_transcoding_error&amp;gt;&lt;/code&gt; for &lt;code&gt;to_utfN_or_error&lt;/code&gt; (prvalue range!)&lt;/li&gt;
&lt;li&gt;value type: same as reference&lt;/li&gt;
&lt;li&gt;category: at most bidirectional&lt;/li&gt;
&lt;li&gt;common: when &lt;code&gt;r&lt;/code&gt; is common&lt;/li&gt;
&lt;li&gt;sized: when &lt;code&gt;r&lt;/code&gt; is sized, its value type is &lt;code&gt;char32_t&lt;/code&gt;, and using &lt;code&gt;to_utf32&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;const-iterable: when &lt;code&gt;r&lt;/code&gt; is const-iterable and either &lt;code&gt;const R&lt;/code&gt; is input and not forward, or &lt;code&gt;r&lt;/code&gt;&apos;s value type is &lt;code&gt;char32_t&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;borrowed: when &lt;code&gt;r&lt;/code&gt; is borrowed&lt;/li&gt;
&lt;li&gt;constant: always&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Other Standard Views&lt;/h2&gt;
&lt;h3&gt;&lt;code&gt;std::filesystem::path_view : [const path_view_component&amp;amp;]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;(Current design as of &lt;a href=&quot;https://wg21.link/P1030R8&quot;&gt;P1030R8&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;&lt;code&gt;path_view&lt;/code&gt; represents a trivially copyable view of explicitly unencoded or encoded character sequences in the format of a native or generic filesystem path. When iterated, it yields a &lt;code&gt;path_view_component&lt;/code&gt; that represents a part of a path not separated by path separators.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; path_view(&quot;/foo/bar&quot;)
[&quot;foo&quot;, &quot;bar&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;reference: &lt;code&gt;const path_view_component&amp;amp;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;value type: &lt;code&gt;path_view_component&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;category: bidirectional&lt;/li&gt;
&lt;li&gt;common: always&lt;/li&gt;
&lt;li&gt;sized: never&lt;/li&gt;
&lt;li&gt;const-iterable: always&lt;/li&gt;
&lt;li&gt;borrowed: never&lt;/li&gt;
&lt;li&gt;constant: always&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;code&gt;std::basic_cstring_view&amp;lt;charT[, traits[, Alloc]]&amp;gt;: [charT&amp;amp;]&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;(Current design as of &lt;a href=&quot;https://wg21.link/P3655R3&quot;&gt;P3655R3&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;A lightweight view of a constant contiguous sequence of &lt;code&gt;charT&lt;/code&gt;s (i.e. a string) with guaranteed null (&lt;code&gt;\0&lt;/code&gt;) termination. Can view C strings (null-terminated &lt;code&gt;const charT*&lt;/code&gt;), &lt;code&gt;std::basic_string&lt;/code&gt;, and many more.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;constraint: &lt;code&gt;charT&lt;/code&gt; must be char-like (non-array trivial standard-layout type), and &lt;code&gt;traits&lt;/code&gt; must be a character trait&lt;/li&gt;
&lt;li&gt;reference: &lt;code&gt;charT&amp;amp;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;value type: &lt;code&gt;charT&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;category: contiguous&lt;/li&gt;
&lt;li&gt;common: always&lt;/li&gt;
&lt;li&gt;sized: always&lt;/li&gt;
&lt;li&gt;const-iterable: always&lt;/li&gt;
&lt;li&gt;borrowed: always&lt;/li&gt;
&lt;li&gt;constant: always&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>String Interpolation For C++: Going Down The Rabbit Hole</title><link>https://mick235711.github.io/2022/08/10/string-interpolation-cpp/</link><guid isPermaLink="true">https://mick235711.github.io/2022/08/10/string-interpolation-cpp/</guid><description>A survey of string interpolation syntax and design choices for C++.</description><pubDate>Wed, 10 Aug 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Recently, EWG reviewed &lt;a href=&quot;https://wg21.link/P1819R0&quot;&gt;P1819R0 Interpolated String Literal&lt;/a&gt;, igniting
a new round of discussion on the possibility of adding string interpolation into C++. The review results
were quite split, and a lot of contentious issues were polled with no consensus in either direction.
Therefore, I want to write a post about all the subtle issues in the idea, and conduct a survey on
how existing languages handle the issue, to try to converge on an agreed way for C++ to go forward.
This is not a proposal, on its own, but may form as a base reading material for future revisions of
P1819 or other proposals.&lt;/p&gt;
&lt;h2&gt;Motivation&lt;/h2&gt;
&lt;h3&gt;What is string interpolation?&lt;/h3&gt;
&lt;p&gt;The term &quot;string interpolation&quot;, at least in this post, refers solely to the feature that allow
you to put placeholders inside a string literals, which are replaced with values when evaluating.
For example, in Python:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apple = 4
print(f&quot;I have {apple} apples.&quot;)  # Output: I have 4 apples.
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;[!NOTE]
In some setting, string interpolation can have an extended meaning in which feature like string concatenation (possibly with &lt;code&gt;1 + &quot; apple&quot;&lt;/code&gt;-like autoboxing) and formatting (&lt;code&gt;std::format&lt;/code&gt; and &lt;code&gt;str.format&lt;/code&gt;) being included; in this post I want to restrain the term to the most strict meaning.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Why?&lt;/h3&gt;
&lt;p&gt;Every EWG and LEWG direction poll are worded very interestingly:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Given our time is limited, and our resources are scarce, EWG encourages further work in the direction of PXXXX?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Intentionally, WG21 groups are using these kind of wording to encourage turning down new proposals, as we must be
very caution to add new feature into the already-complex-enough-language-mess that is C++. So is there a compelling
reason to add yet another kind of string literal into C++?&lt;/p&gt;
&lt;p&gt;I think there is.&lt;/p&gt;
&lt;p&gt;Being used to these &quot;f-strings&quot; in Python, I found them shine especially bright in the context of debugging
and logging. For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def connect(ip: str, port: int) -&amp;gt; None:
    print(f&quot;Connecting to {ip}:{port}...&quot;)

for i in range(1000):
    print(f&quot;[{i:3}/1000] Result = {result()}...&quot;)

# Output:
# [  1/1000] Result = 0.1...
# [  2/1000] Result = 0.3...
# ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It is also useful in constructing strings that embed other information,
such as &lt;code&gt;__repr__&lt;/code&gt;/&lt;code&gt;__str__&lt;/code&gt; methods:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Student:
    def __init__(self, name: str, age: int) -&amp;gt; None:
        self.name, self.age = name, age

    def __repr__(self) -&amp;gt; str:
        return f&apos;&amp;lt;Student {self.name} with age {self.age}&amp;gt;&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;But don&apos;t we already have &lt;code&gt;std::format&lt;/code&gt;?&lt;/h3&gt;
&lt;p&gt;Yes, and I&apos;m also aware that there is proposals to add &lt;code&gt;constexpr&lt;/code&gt; formatting capabilities
to &lt;code&gt;std::format&lt;/code&gt;. However, &lt;code&gt;std::format&lt;/code&gt;, as a library feature, does not come close to the
user-friendliness and convenience brought by string interpolation. Compare:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;std::format(&quot;&amp;lt;Student {} with age {}&amp;gt;&quot;, name, age);
f&quot;&amp;lt;Student {name} with age {age}&amp;gt;&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I do think that interpolated literals bring the variables and expressions to be replaced directly
into the place they belong. In the formatting example, we still need to manually map the positioning
of &lt;code&gt;{}&lt;/code&gt;s and variables in our mind. It is even worse if we specify positional argument in &lt;code&gt;std::format&lt;/code&gt;,
as we need to manually re-position all the variables.&lt;/p&gt;
&lt;p&gt;Of course, this is not to say that string interpolation is a direct replacement of &lt;code&gt;std::format&lt;/code&gt;.
There is still the advantage of i18n localized formatted strings, in which we can provide &lt;code&gt;{0} {1}&lt;/code&gt;
in one language and &lt;code&gt;{1} {0}&lt;/code&gt; in another language where grammar were reversed. Another
advantage is the ability of repeating positional specifier to repeat a variable.&lt;/p&gt;
&lt;p&gt;Both EWGI and EWG had expressed support in a future C++ string interpolation facility, so it is worthwhile
to have a look at how other language did it, and find a good way for C++ to process forward.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;EWGI (2019-07 Cologne): Spend committee time on this vs other proposals given that time is limited?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;SF&lt;/th&gt;
&lt;th&gt;F&lt;/th&gt;
&lt;th&gt;N&lt;/th&gt;
&lt;th&gt;A&lt;/th&gt;
&lt;th&gt;SA&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;EWG (2022-08-04): Given our time is limited, and our resources are scarce, EWG Encourages further work in the direction of P1819.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;SF&lt;/th&gt;
&lt;th&gt;F&lt;/th&gt;
&lt;th&gt;N&lt;/th&gt;
&lt;th&gt;A&lt;/th&gt;
&lt;th&gt;SA&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;Result: Consensus&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Survey&lt;/h2&gt;
&lt;p&gt;At a glance, string interpolation seems a pretty simple feature: just factor out all the expressions
and rewrite to a &lt;code&gt;std::format&lt;/code&gt; call, right? However, there are a surprisingly large number of subtle
issues involved in this concept, with most of them no clear answer. Therefore, I want to take a practical
approach, or what WG21 often describe as &quot;standardizing existing practice&quot;... take a look at all the language
that already have string interpolation ability, and look at how they solve the issues.&lt;/p&gt;
&lt;p&gt;The language chosen are taken from the &lt;a href=&quot;https://en.wikipedia.org/wiki/String_interpolation&quot;&gt;string interpolation Wikipedia page&lt;/a&gt;,
with a total of 27 different languages: ABAP, Bash, Boo, C#, ColdFusion, CoffeeScript,
Dart, Groovy, Haxe, JavaScript, Julia, Kotlin, Nemerle, Nim, Nix, ParaSail,
Perl, PHP, Python, Ruby, Rust, Scala, Sciter, Swift, Tcl, TypeScript and Visual Basic.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Language&lt;/th&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;Syntax&lt;/th&gt;
&lt;th&gt;Expression&lt;/th&gt;
&lt;th&gt;Format&lt;/th&gt;
&lt;th&gt;Link&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ABAP&lt;/td&gt;
&lt;td&gt;Dynamic (DSL)&lt;/td&gt;
&lt;td&gt;`&lt;/td&gt;
&lt;td&gt;{...}&lt;/td&gt;
&lt;td&gt;`&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bash&lt;/td&gt;
&lt;td&gt;Dynamic&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&quot;$...&quot;&lt;/code&gt;, &lt;code&gt;&quot;${...}&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;Many&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://www.gnu.org/software/bash/manual/bash.html#Shell-Parameter-Expansion&quot;&gt;Shell Parameter Expansion&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Boo&lt;/td&gt;
&lt;td&gt;Dynamic&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&quot;$...&quot;&lt;/code&gt;, &lt;code&gt;&quot;$(...)&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://github.com/boo-lang/boo/wiki/String-Interpolation&quot;&gt;String Interpolation&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C#&lt;/td&gt;
&lt;td&gt;Static (VM)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;$&quot;{...}&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{...,...:...}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/interpolated&quot;&gt;String Interpolation&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ColdFusion&lt;/td&gt;
&lt;td&gt;Dynamic&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&quot;#...#&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://coldfusion.adobe.com/2021/04/coldfusion-101-tags-script-functions-part-3-functions/&quot;&gt;Parsed by Server&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CoffeeScript&lt;/td&gt;
&lt;td&gt;Dynamic&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&quot;#{...}&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;http://coffeescript.org/#strings&quot;&gt;Strings&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dart&lt;/td&gt;
&lt;td&gt;Static (VM)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&apos;$...&apos;&lt;/code&gt;, &lt;code&gt;&apos;${...}&apos;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://dart.dev/guides/language/language-tour#strings&quot;&gt;Strings&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Groovy&lt;/td&gt;
&lt;td&gt;Both&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&quot;$...&quot;&lt;/code&gt;, &lt;code&gt;&quot;${...}&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://groovy-lang.org/syntax.html#_string_interpolation&quot;&gt;GString Interpolation&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Haxe&lt;/td&gt;
&lt;td&gt;Static (VM)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&apos;$...&apos;&lt;/code&gt;, &lt;code&gt;&apos;${...}&apos;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://haxe.org/manual/lf-string-interpolation.html&quot;&gt;String Interpolation&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JavaScript&lt;/td&gt;
&lt;td&gt;Dynamic&lt;/td&gt;
&lt;td&gt;&lt;code&gt;`${...}`&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals&quot;&gt;Template Literals&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Julia&lt;/td&gt;
&lt;td&gt;Dynamic&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&quot;$...&quot;&lt;/code&gt;, &lt;code&gt;&quot;$(...)&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://docs.julialang.org/en/v1/manual/strings/#string-interpolation&quot;&gt;String Interpolation&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kotlin&lt;/td&gt;
&lt;td&gt;Static (VM)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&quot;$...&quot;&lt;/code&gt;, &lt;code&gt;&quot;${...}&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://kotlinlang.org/docs/basic-types.html#string-templates&quot;&gt;String Templates&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nemerle&lt;/td&gt;
&lt;td&gt;Static (VM)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;$&quot;$...&quot;&lt;/code&gt;, &lt;code&gt;$&quot;$(...)&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://github.com/rsdn/nemerle/wiki/Features#string-interpolation&quot;&gt;String Interpolation&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nim&lt;/td&gt;
&lt;td&gt;Static&lt;/td&gt;
&lt;td&gt;&lt;code&gt;fmt&quot;{...}&quot;&lt;/code&gt;, &lt;code&gt;&amp;amp;&quot;{...}&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{...[=]:...}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://nim-lang.org/docs/strformat.html&quot;&gt;std/strformat Module&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nix&lt;/td&gt;
&lt;td&gt;Dynamic (DSL)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&quot;${...}&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://nixos.org/manual/nix/stable/expressions/language-values.html&quot;&gt;Nix Values: Antiquotation&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ParaSail&lt;/td&gt;
&lt;td&gt;Both&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&quot;`(...)`&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://adacore.github.io/ParaSail/images/parasail_ref_manual.pdf&quot;&gt;ParaSail Reference Manual&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Perl&lt;/td&gt;
&lt;td&gt;Dynamic&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&quot;$...&quot;&lt;/code&gt;, &lt;code&gt;&quot;@{...}&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Only Array&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://perldoc.perl.org/perldata#Array-Interpolation&quot;&gt;Array Interpolation&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PHP&lt;/td&gt;
&lt;td&gt;Dynamic&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&quot;$...&quot;&lt;/code&gt;, &lt;code&gt;&quot;{...}&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://www.php.net/manual/en/language.types.string.php#language.types.string.parsing&quot;&gt;Variable Parsing&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Python&lt;/td&gt;
&lt;td&gt;Dynamic&lt;/td&gt;
&lt;td&gt;&lt;code&gt;f&quot;{...}&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{...[=]!.:...}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://docs.python.org/3/reference/lexical_analysis.html#f-strings&quot;&gt;Formatted String Literals&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ruby&lt;/td&gt;
&lt;td&gt;Dynamic&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&quot;#{...}&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://ruby-doc.org/core-3.1.2/doc/syntax/literals_rdoc.html#label-String+Literals&quot;&gt;String Literals&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rust&lt;/td&gt;
&lt;td&gt;Static&lt;/td&gt;
&lt;td&gt;&lt;code&gt;println!(&quot;{...}&quot;)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{...:...}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://doc.rust-lang.org/stable/std/fmt/#named-parameters&quot;&gt;Named Parameters&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scala&lt;/td&gt;
&lt;td&gt;Static (VM)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;s&quot;$...&quot;&lt;/code&gt;, &lt;code&gt;s&quot;${...}&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://docs.scala-lang.org/overviews/core/string-interpolation.html&quot;&gt;String Interpolation&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sciter&lt;/td&gt;
&lt;td&gt;Dynamic (DSL)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;$fun({...})&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://sciter.com/docs/content/script/language/Functions.htm#Stringizer&quot;&gt;Stringizer Functions&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Swift&lt;/td&gt;
&lt;td&gt;Static&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&quot;\(...)&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://docs.swift.org/swift-book/LanguageGuide/StringsAndCharacters.html#ID292&quot;&gt;String Interpolation&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tcl&lt;/td&gt;
&lt;td&gt;Dynamic&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&quot;$...&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;http://tmml.sourceforge.net/doc/tcl/Tcl.html&quot;&gt;Variable Substitution&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TypeScript&lt;/td&gt;
&lt;td&gt;Dynamic&lt;/td&gt;
&lt;td&gt;&lt;code&gt;`${...}`&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;Same as JavaScript&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Visual Basic&lt;/td&gt;
&lt;td&gt;Static (VM)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;$&quot;{...}&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{...,...:...}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/visual-basic/programming-guide/language-features/strings/interpolated-strings&quot;&gt;Interpolated Strings&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Syntax Design&lt;/h2&gt;
&lt;h3&gt;Big Picture&lt;/h3&gt;
&lt;p&gt;First, let&apos;s have a look at the general syntax components of a interpolated string literal.
They, in general, looks somewhat like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;f&quot;Other things {myVar:.2} other things&quot;
| ^^^^^^^^^^^^^||||||||||^^^^^^^^^^^^^ string component
|              ^||||||||^ delimeter
|               |||||^^^ formatter
|               ^^^^^ expression
^ introducer
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Such literals usually composed of five parts: introducer
(some language use special quotation mark like backticks, those count as introducer too),
expression, formatter, delimeter, and string component. Often, some parts may be missing (like language that
does not support formatting will not have formatter), but in this post we will take a look at each of the components,
and their different appearance in each language.&lt;/p&gt;
&lt;p&gt;Let&apos;s break down the easy part first. The string component is, obviously, the same for all language, these are just regular
strings. Other parts are much more complicated, and will be discussed from inside to outside.&lt;/p&gt;
&lt;h3&gt;Expression&lt;/h3&gt;
&lt;p&gt;Expression is the part where you specify the variables or expressions that you want to substitute in.
There, most language just simply agrees that any expression can be put here, for example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apple = 3
print(f&quot;I have {apple} apples.&quot;)  # I have 3 apples.
print(f&quot;I have {apple + 1} apples.&quot;)  # I have 4 apples.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice that often, arbitrary expression support is required for convenience of this feature
(which is really all it is about, it is just a syntactic sugar), for example &lt;code&gt;apples[0]&lt;/code&gt;,
&lt;code&gt;get_apples()&lt;/code&gt; being substituted are very common case that appears in many real-world code.
Which is why most language agree that any expression can appear here.&lt;/p&gt;
&lt;p&gt;However, some language take a different route: only allow a variable name here, nothing else.
Or in C++-speak, only allow a single &lt;em&gt;id-expression&lt;/em&gt; here:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let apple = 3;
println!(&quot;I have {apple} apples.&quot;);  // Okay
println!(&quot;I have {apple + 1} apples.&quot;);  // Error!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On surface, this seems an arbitrary restriction, and really had prevented some useful use case
like subscripting and function call. Natuarally, only a handful of language take this route.
In the above table we can see that only Bash, Tcl and Rust have this restriction, while Bash
and Tcl are both dynamic scripting language that naturally only support &lt;code&gt;$variable&lt;/code&gt; as variable
substitution and nothing else. This leaves Rust as the only language that only support variable
substitution, and in &lt;a href=&quot;https://rust-lang.github.io/rfcs/2795-format-args-implicit-identifiers.html&quot;&gt;RFC 2795&lt;/a&gt;,
the author explained the rationale:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If any expressions beyond identifiers become accepted in format strings, then the RFC author expects that users
will inevitably ask &quot;why is my particular expression not accepted?&quot;. This could lead to feature creep, and
before long perhaps the following might become valid Rust:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;println!(&quot;hello { if self.foo { &amp;amp;self.person } else { &amp;amp;self.other_person } }&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;This no longer seems easily readable to the RFC author.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In short, the reason that Rust does not allow anything above variable name is that allowing arbitrary expression
may leads to very complex expression being present, which is a bad style as the string is no longer easily readable.
This is a real problem, as many people may have written &lt;code&gt;None&lt;/code&gt;-related conditionals in Python:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;result = get_int()  # may return int or None
print(f&quot;Got {result if result is not None else 0} as result.&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This had already becoming hard to read, and if we introduce the same syntax into C++, the problem may become worse:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;std::optional&amp;lt;int&amp;gt; get_int();
auto result = get_int();
std::println(f&quot;Got { get_int().transform([](auto a){ return a * 2; }).value_or(0) } as result.&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, I personally don&apos;t think that this potential danger is worth discarding the great benefit that allowing
&lt;code&gt;get_int()&lt;/code&gt;, &lt;code&gt;arr[2]&lt;/code&gt; etc had given us. The author of &lt;a href=&quot;https://peps.python.org/pep-0498/&quot;&gt;PEP 498&lt;/a&gt; had rightfully
pointed out regarding this issue:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;While it’s true that very ugly expressions could be included in the f-strings, this PEP takes the position that
such uses should be addressed in a linter or code review.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I personally agree that this should just be a Core Guideline issue to not use long placeholders. In EWG review
of P1819R0 on 2022-08-04, WG21 also agrees with the decision that a future string interpolation facility in C++
should support arbitrary expression:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;EWG encourages more work in the direction of supporting arbitrary expressions, instead of just ID expressions.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;SF&lt;/th&gt;
&lt;th&gt;F&lt;/th&gt;
&lt;th&gt;N&lt;/th&gt;
&lt;th&gt;A&lt;/th&gt;
&lt;th&gt;SA&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;Result: Consensus&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So I think that this issue had been solved.&lt;/p&gt;
&lt;h3&gt;Formatter&lt;/h3&gt;
&lt;p&gt;The formatter component refer to the additional specifier after the expression, to format it.
For example, we can add some specification to make the expression result to have a fixed width:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;for i in range(1000):
    print(f&quot;[{i:3}/1000] Hello!&quot;)
# Print:
# [  1/1000] Hello!
# [  2/1000] Hello!
# ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These formatter specifiers had had a long history. Their first common-known appearance is probably
the C &lt;code&gt;printf&lt;/code&gt;/&lt;code&gt;scanf&lt;/code&gt; family of functions, in where type specifier like &lt;code&gt;%d&lt;/code&gt; and fill/width specifier
like &lt;code&gt;%03d&lt;/code&gt; are introduced. Later, Python improved these specifier by changing to a &lt;code&gt;{}&lt;/code&gt; format, eliminating
the need for always specifying type specifiers, and also changed alignment specifier to a more straightforward
&lt;code&gt;&amp;lt;&amp;gt;^&lt;/code&gt; system. This new type of formatter had been adopted by many languages coming forward, including C++ &lt;code&gt;std::format&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;However, contrary to the general acceptance of arbitrary expression for the expression part, there are very few
languages that actually support formatters in string interpolation. From the above table, we can see that only
6 languages (Bash, C#, Nim, Python, Rust, Visual Basic) support some kind of formatter. There are two main reason for
the general reluctancy of formatters:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Formatters often make the interpolation string looks more complicated and hard to read. Similar to the situation of
complicated expression, complex formatter can also hinder readability:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;vector ints{65192, 65535, 766, 8687, 65524, 14386};
std::println(f&quot;Your IPv6 address is {ints:nd[:]:04x}.&quot;);
// Your IPv6 address is fea8:ffff:02fe:21ef:fff4:3832.
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;Interpolated string literals with formatters are harder to implement than those without. Among the language that does not
support formatter, many are reluctant to support it because they didn&apos;t even have a &lt;code&gt;str.format&lt;/code&gt;-like function, thus do not
have any existing facility to utilize formatters. Furthermore, even among those who has formatting functions already, a simple
interpolation without formatter, like &lt;code&gt;f&quot;I have {apple} apples&quot;&lt;/code&gt;, can simply be translated into a concatenation&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;&quot;I have &quot; + apple + &quot; apples&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;which (assuming &lt;code&gt;toString()&lt;/code&gt;&apos;s existence or implicit calling of such method) can be an easy task for the compiler. However, with
formatter present, either we must resort to Nim&apos;s approach to translate to something like&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot;I have&quot; + std::format(&quot;{:02}&quot;, apple) + &quot; apples&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or translate directly into a single big &lt;code&gt;format&lt;/code&gt; call:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;std::format(&quot;I have {:02} apples&quot;, apple)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, both reason does not apply to C++ at all. In C++, we already have the formatting infrastructure provided by &lt;code&gt;std::format&lt;/code&gt;,
so we can simply support the same &lt;em&gt;format-specifier&lt;/em&gt; and all is well. Also, even if we restrict to no-formatter mode, we &lt;strong&gt;still&lt;/strong&gt;
cannot use simple concatenation to translate interpolated strings, because in C++ we have no general &lt;code&gt;toString()&lt;/code&gt; method at all!
&lt;code&gt;std::to_string&lt;/code&gt; only works for arithmetic types, so the only general way we can get a string from any object is through
&lt;code&gt;std::format(&quot;{}&quot;, obj)&lt;/code&gt;, which basically means that the support for formatters is already there, and dropping them will have
absolutely no performance gain. As for readability, I support the same argument as ones for the complicated expression concern,
namely this is a code review issue, not an issue that prevent us to support even the simplest formatting specifier.&lt;/p&gt;
&lt;p&gt;Having decided that C++ have sufficient reason to support formatters, let&apos;s have a look at their syntax in existing language.
For C# and VB, their .NET format specifiers are not taken from Python, and instead have taken a form that looks like &lt;code&gt;{,[align]:[type][prec]}&lt;/code&gt;,
so their string interpolation facility also support the same &lt;code&gt;{...,...:...}&lt;/code&gt; format. This is, in fact, consistent with Rust, Python and Nim&apos;s &lt;code&gt;{...:...}&lt;/code&gt;
choice, because the only difference is that C# and VB move the &lt;code&gt;[align]&lt;/code&gt; part from after the colon to before the colon.
Apart from these standard specifiers, there are a few creative additions for Python and Nim. Python allow a &lt;code&gt;!s&lt;/code&gt;, &lt;code&gt;!r&lt;/code&gt; or &lt;code&gt;!a&lt;/code&gt; specifier to
appear immediately before the colon, whose effect is to call &lt;code&gt;str()&lt;/code&gt;, &lt;code&gt;repr()&lt;/code&gt; or &lt;code&gt;ascii()&lt;/code&gt; before formatting. In &lt;a href=&quot;https://peps.python.org/pep-0498/&quot;&gt;PEP 498&lt;/a&gt;,
the author actually admitted that this is just for compatibility with &lt;code&gt;str.format&lt;/code&gt;, and on their own are redundant specifiers:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;!s&lt;/code&gt;, &lt;code&gt;!r&lt;/code&gt;, and &lt;code&gt;!a&lt;/code&gt; conversions are not strictly required. Because arbitrary expressions are allowed inside the f-strings, [...]
However, &lt;code&gt;!s&lt;/code&gt;, &lt;code&gt;!r&lt;/code&gt;, and &lt;code&gt;!a&lt;/code&gt; are supported by this PEP in order to minimize the differences with &lt;code&gt;str.format()&lt;/code&gt;. &lt;code&gt;!s&lt;/code&gt;, &lt;code&gt;!r&lt;/code&gt;, and &lt;code&gt;!a&lt;/code&gt; are
required in &lt;code&gt;str.format()&lt;/code&gt; because it does not allow the execution of arbitrary expressions.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Therefore, since in C++ we don&apos;t have such tradition in &lt;code&gt;std::format&lt;/code&gt;, and there is no general string-conversion function like &lt;code&gt;str()&lt;/code&gt; anyway,
I see no reason to introduce &lt;code&gt;!...&lt;/code&gt; part into C++ interpolated strings. However, another addition introduced by both Python and Nim, the &lt;code&gt;=&lt;/code&gt;
part before the colon, is more interesting. The effect of such a &lt;code&gt;=&lt;/code&gt; is to introduce a debugging format, in which the expression text will be displayed
alongside its value:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a = 3
print(f&quot;I have {a=}&quot;)  # I have a=3
print(f&quot;I have {a = }&quot;)  # I have a = 3
print(f&quot;I have {a  =:02}&quot;)  # I have a  =03
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Basically, this is a shorthand for the common practice of var = value trick in debugging prints. In my personal opinion, I think that this specifier
is very appealing, but at the same time it can be added later, after general interpolation is introduced (in Python, = is added two versions after f-strings anyway).
Also, this specifier has its own problem in C++ (will be described below in the issues section), so I suggest holding off its addition into separate proposal.&lt;/p&gt;
&lt;p&gt;In conclusion, I suggest that C++ string interpolation facility should support &lt;em&gt;format-specifier&lt;/em&gt;s, as they are naturally supported as a result of the implementation
strategy, and also the formatter format should (for now) simply be &lt;code&gt;{...:...}&lt;/code&gt;, the same as &lt;code&gt;std::format&lt;/code&gt;. The Python/Nim = specifier can be added later in a separate
proposal, once its issues are solved. (Technically, given WG21&apos;s favor for minimal proposals these days, support for formatters can also be added later, as colons are not
used much in C++ expressions; however I felt like that would be too minimal for a first proposal).&lt;/p&gt;
&lt;h3&gt;Delimeter&lt;/h3&gt;
&lt;p&gt;Delimeters are a natural requirement to the proposed interpolation syntax, especially for those language that support interpolating arbitrary expressions. Even for those that
only support interpolating variables, the possibility of ambiguity still calls for a need of delimeter:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;fruit=apple
echo &quot;I want some $fruit&quot;  # I want some apple
echo &quot;I want some $fruits&quot;  # I want some (unknown variable not displayed)
echo &quot;I want some ${fruit}s&quot;  # I want some apples
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, there is a catch: many languages (including Bash) support both a mode without delimeter and a mode with one, to give more convenience
to the user. The general rule here is that without a delimeter, the expression part will start from the introducer and match greedily, often
only stop when meeting a whitespace or end of string. So in the above example, &lt;code&gt;$fruit&lt;/code&gt; can work but &lt;code&gt;$fruits&lt;/code&gt; cannot. These languages with two modes
are recorded as none plus some delimeter type below, where none signals the &lt;code&gt;$fruit&lt;/code&gt; case without delimeter.&lt;/p&gt;
&lt;p&gt;As for the delimeter themselves, there are many choice:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;None + &lt;code&gt;{}&lt;/code&gt;: (8) Bash, Dart, Groovy, Haxe, Kotlin, Perl, PHP, Scala&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{}&lt;/code&gt; only: (12) ABAP, C#, CoffeeScript, JavaScript, Nim, Nix, Python, Ruby, Rust, Sciter, TypeScript, Visual Basic&lt;/li&gt;
&lt;li&gt;None + &lt;code&gt;()&lt;/code&gt;: (3) Boo, Julia, Nemerle&lt;/li&gt;
&lt;li&gt;&lt;code&gt;()&lt;/code&gt; only: (2) ParaSail, Swift&lt;/li&gt;
&lt;li&gt;&lt;code&gt;#&lt;/code&gt; only: (1) ColdFusion&lt;/li&gt;
&lt;li&gt;No delimeter (only support greedy variable interpolation): (1) Tcl&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We can easily see that there are only two common choice of delimeter: &lt;code&gt;{}&lt;/code&gt; and &lt;code&gt;()&lt;/code&gt;. In which &lt;code&gt;{}&lt;/code&gt; have 20 language users, while
&lt;code&gt;()&lt;/code&gt; only have 5, so &lt;code&gt;{}&lt;/code&gt; is the overwhelmingly favorite. Also, C++ &lt;code&gt;std::format&lt;/code&gt; already used &lt;code&gt;{}&lt;/code&gt; as delimeter, so I think that there
should be no controversy on the choice for C++: just continue to use &lt;code&gt;{}&lt;/code&gt; as delimeter. (One can also argue that both &lt;code&gt;{}&lt;/code&gt; and &lt;code&gt;()&lt;/code&gt; have had
precedent in C++, with the latter being the delimeter used for raw strings; but I think consistency with &lt;code&gt;std::format&lt;/code&gt; is much more important.)
However, noted that in C++ we have to use a prefix introducer (more on this below), so we cannot introduce the greedy no-delimeter
matching facility, forcing C++ to be in the &lt;code&gt;{}&lt;/code&gt; only group (it is the group with most people anyway).&lt;/p&gt;
&lt;h3&gt;Introducer&lt;/h3&gt;
&lt;p&gt;Now comes the fun part.&lt;/p&gt;
&lt;p&gt;Introducers are also one of the required feature of any string interpolation facility, as you have to distinguish interpolated string from regular
string in some way to process them differently. However, there are four general categories of languages with regard to introducers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Prefix, in which introducer are placed before the string, like &lt;code&gt;f&quot;something&quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Adjacent, in which introducer are placed immediately before delimeter, like &lt;code&gt;&quot;something ${var}&quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Quote, in which introducer are the quotes themselves (i.e. special quote is used), like &lt;code&gt;|something|&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Function, in which interpolation is only available as parameters to certain functions&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Adjacent is more common in scripting or dynamic languages, in which variables are often also referred as &lt;code&gt;$var&lt;/code&gt;. It is also worth noting that the
aforementioned greedy no-delimeter substitution feature is only available with language in the adjacent group. The languages can be categorized as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Prefix: (4) C#, Visual Basic (both &lt;code&gt;$&lt;/code&gt;), Python (&lt;code&gt;f&lt;/code&gt;), Nim (&lt;code&gt;fmt&lt;/code&gt; and &lt;code&gt;&amp;amp;&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Adjacent &lt;code&gt;$&lt;/code&gt;: (9) Bash, Boo, Dart, Groovy, Haxe, Julia, Kotlin, Nix, Tcl&lt;/li&gt;
&lt;li&gt;Adjacent &lt;code&gt;#&lt;/code&gt;: (3) ColdFusion (double &lt;code&gt;#&lt;/code&gt;), CoffeeScript, Ruby&lt;/li&gt;
&lt;li&gt;Adjacent other: (4) ParaSail (double &lt;code&gt;`&lt;/code&gt;), Perl (&lt;code&gt;$&lt;/code&gt; and &lt;code&gt;@&lt;/code&gt;), PHP (&lt;code&gt;$&lt;/code&gt; or None), Swift (&lt;code&gt;\&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Both Adjacent and Prefix: (2) Nemerle (both &lt;code&gt;$&lt;/code&gt;), Scala (&lt;code&gt;s&lt;/code&gt; prefix and &lt;code&gt;$&lt;/code&gt; adjacent)&lt;/li&gt;
&lt;li&gt;Quote: (3) ABAP (&lt;code&gt;|&lt;/code&gt;), JavaScript, TypeScript (both &lt;code&gt;`&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Function: (2) Rust (&lt;code&gt;println!&lt;/code&gt; family), Sciter (anything start with &lt;code&gt;$&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Overall there are 6 Prefix, 18 Adjacent, 3 Quote and 2 Function, with Adjacent group being the overwhelmingly favorite. The reason for that result, I think,
is that generally we want the interpolated (substituted) part to be as distinct as possible from the rest of the string, in order for reader to quickly
realize that this part will be substituted, and add an introducer here can be a good way to remind them. Also, the Adjacent placement also enable the possibility
of no-delimeter, which over half of the 18 languages had utilized.&lt;/p&gt;
&lt;p&gt;However, the Adjacent group have its own great limitation, which makes it unsuitable for C++: backward compatibility. We already have strings that contain &lt;code&gt;$&lt;/code&gt; in C++,
and changing it to perform interpolation is simply a non-starter because it will break way too much legacy code. All the Adjacent group languages have had interpolation
since their first version, so there is no risk of breaking code. However, for C++, we must reject all Adjacent approach... except for one! The Swift Adjacent (&lt;code&gt;&quot;\(...)&quot;&lt;/code&gt;)
is actually still viable for C++, given that &lt;code&gt;\(&lt;/code&gt; is an unused escape sequence. However, currently all major C++ compilers will only produce a warning for unknown escape
sequences, and then proceed as if the backslash isn&apos;t here. Therefore if we want to be secure, we must first deprecate &lt;code&gt;\(&lt;/code&gt; for at least one standard, and then reuse it.
(Given that there is very unlikely to be large number of code with this escape outside in the wild, I do think that we can skip the deprecation period and directly reuse this as
interpolation; however the Swift syntax does not look very appealing anyway). For those reasons, I will suggest that the whole Adjacent group is unsuitable for C++.&lt;/p&gt;
&lt;p&gt;The Function group also deserves a closer look. For Rust, the &lt;code&gt;println!&lt;/code&gt; family (to be precise, the target is actually the &lt;code&gt;format_args!&lt;/code&gt; family) are already implemented as macros,
which means that it is possible to customize their argument behavior as a library feature, no need for core language change. Therefore, Rust can happily limit the interpolation scope
to only the &lt;code&gt;format_args!&lt;/code&gt; family, and unuseable for anything else. Sciter also took an interesting strategy: any function with name starting from &lt;code&gt;$&lt;/code&gt; will treats its argument as literal string,
and anything inside &lt;code&gt;{}&lt;/code&gt; inside such an argument will simply be left out unquoted:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var bodyDiv = self.$(div#body);
// equivalent to
var bodyDiv = self.$(&quot;div#body&quot;);

var nthDiv = self.$(div:nth-child({n}));
// equivalent to
var nthDiv = self.$(&quot;div:nth-child(&quot;, n, &quot;)&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This way, the function can itself concatenate all the argument to form the interpolated string. However, both languages&apos; approach cannot apply to C++: &lt;code&gt;std::format&lt;/code&gt; is not a macro, and we do not want
to limit string interpolation to &lt;code&gt;std::format&lt;/code&gt; and &lt;code&gt;std::print&lt;/code&gt; family anyway; and introducing &lt;code&gt;$&lt;/code&gt;-functions will simply be too much a change for C++ to handle. It is viable, but it would simply be too
radical, as the whole grammar need to change dramatically to allow this.&lt;/p&gt;
&lt;p&gt;This only leaves Prefix and Quote as routes for C++. Both of these routes are viable, but I want to argue that Quote group have its own inherent limitation in C++ too: no existing practice. In C++, we have always had only
two kind of quotations, single and double for &lt;code&gt;char&lt;/code&gt; and &lt;code&gt;const char*&lt;/code&gt;. Introducing the third kind of quotation for solely the purpose of string interpolation seems a bit weird and aggressive, and also having a third
kind of quotation without a third kind of type (it will evaluate to &lt;code&gt;std::string&lt;/code&gt; probably anyway) also seems weird to me. On the contrary, in C++ we have had prefix specifiers for years now (encoding and raw strings),
so adding a new kind of prefix specifier is not a radical change. Therefore, I personally support Prefix as the way for C++ to go.&lt;/p&gt;
&lt;p&gt;However, inside the Prefix group the syntax is very split in different languages (I&apos;m actually surprised that no one else used f-strings). C++ will face a choice here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;$&lt;/code&gt;, the advantage is that there will be absolutely no possibility of breaking existing code, while the disadvantage will be to introduce a new novel syntax (&lt;code&gt;$&lt;/code&gt; have no appearance in C++ yet)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;f&lt;/code&gt;, the advantage is that this will require no novel syntax learning, it is just similar to &lt;code&gt;u&lt;/code&gt; and &lt;code&gt;R&lt;/code&gt; prefix, convey clearly the &quot;formatting&quot; meaning,
and also does not introduce new symbol into C++ glossary; however the disadvantage is that there is a small possibility of breaking existing code with macro named &lt;code&gt;f&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Other one-character prefix, the analyze is same with &lt;code&gt;f&lt;/code&gt;, with the additional disadvantage that it has no clear link with &quot;formatting&quot; meaning&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fmt&lt;/code&gt; (or &lt;code&gt;format&lt;/code&gt;, etc), the advantage is that the meaning is conveyed most clearly, however a big disadvantage is that this will be more clumsy to type (shorter typing
is the sole reason we introduce string interpolation anyway), and also have a bigger possibility of macro conflicting.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Overall, I don&apos;t see a clear winner. Personally, I think that &lt;code&gt;f&lt;/code&gt; makes the most sense for C++, as it has the least disadvantages among the options (macro named &lt;code&gt;f&lt;/code&gt; is increasingly rare anyway, and this problem is also
faced by &lt;code&gt;u&lt;/code&gt; and &lt;code&gt;R&lt;/code&gt; prefix too, they solved it, kinda). &lt;code&gt;$&lt;/code&gt; can be a close second or even first if one day C++ introduced an operator with &lt;code&gt;$&lt;/code&gt; in it so that it is no longer novel, and other choice I think is clearly worse.&lt;/p&gt;
&lt;p&gt;So, in conclusion, my personal suggestion for C++ is the &lt;code&gt;f&quot;{...}&quot;&lt;/code&gt; syntax, with support for &lt;em&gt;format-specifier&lt;/em&gt;s (or in other words, copy Python).&lt;/p&gt;
&lt;h2&gt;General design issues&lt;/h2&gt;
&lt;p&gt;These are the issues that applys to any string interpolation facility, not unique to C++.&lt;/p&gt;
&lt;h3&gt;Implementation: eager or lazy?&lt;/h3&gt;
&lt;h3&gt;Escaping behaviour&lt;/h3&gt;
&lt;p&gt;Now this is the most difficult and contentious part of any such facility.&lt;/p&gt;
&lt;h3&gt;Customization&lt;/h3&gt;
&lt;h2&gt;Issues specifically for C++&lt;/h2&gt;
&lt;p&gt;Of course, being one of the most complex language in the world, a string interpolation facility for C++ will face
its own bunch of issues, specifically because of its interaction with other features or limitations of C++.&lt;/p&gt;
&lt;h3&gt;Availability of &lt;code&gt;@&lt;/code&gt;, &lt;code&gt;$&lt;/code&gt; and &lt;code&gt;`&lt;/code&gt;&lt;/h3&gt;
&lt;h3&gt;What is the type of interpolated string?&lt;/h3&gt;
&lt;h3&gt;Macro conflict&lt;/h3&gt;
&lt;h3&gt;Ambiguity of &lt;code&gt;=&lt;/code&gt; specifier&lt;/h3&gt;
&lt;h3&gt;(Non-existent?) issues with &lt;em&gt;format-specifier&lt;/em&gt; support&lt;/h3&gt;
&lt;h3&gt;Concatenation&lt;/h3&gt;
&lt;h3&gt;Interaction with other prefix&lt;/h3&gt;
&lt;h3&gt;Interaction with UDL&lt;/h3&gt;
&lt;h3&gt;&lt;code&gt;constexpr&lt;/code&gt; interpolated strings&lt;/h3&gt;
&lt;h3&gt;Translation stage&lt;/h3&gt;
&lt;h3&gt;Implementation difficulty&lt;/h3&gt;
&lt;h2&gt;Wording&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;[!NOTE]
This wording is just an initial attempt and is known to be incorrect and incomplete, if this were to be a proposal, the wording is probably in need of an overhaul. Wording is based on &lt;a href=&quot;https://wg21.link/N5054&quot;&gt;N5054&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded></item><item><title>How are my WG21 proposals doing?</title><link>https://mick235711.github.io/2022/07/07/wg21-proposal-tracker/</link><guid isPermaLink="true">https://mick235711.github.io/2022/07/07/wg21-proposal-tracker/</guid><description>A tracker for Yihe Li&apos;s C++ standardization proposals.</description><pubDate>Thu, 07 Jul 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I have written several proposals for the ISO C++ committee (WG21). They are all written in
&lt;a href=&quot;https://tabatkins.github.io/bikeshed/&quot;&gt;Bikeshed&lt;/a&gt; so that automatic proposal referencing can be used,
and the resulting proposal looks nicer. All proposal source code are stored in the &lt;a href=&quot;https://github.com/Mick235711/wg21-papers&quot;&gt;wg21-papers&lt;/a&gt;
repo.&lt;/p&gt;
&lt;h4&gt;P2549: &lt;code&gt;std::unexpected&amp;lt;E&amp;gt;&lt;/code&gt; should have &lt;code&gt;error()&lt;/code&gt; as member accessor&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Audience: LWG&lt;/li&gt;
&lt;li&gt;Target: C++23&lt;/li&gt;
&lt;li&gt;Revisions: &lt;a href=&quot;https://wg21.link/P2549R0&quot;&gt;R0&lt;/a&gt;, &lt;a href=&quot;https://wg21.link/P2549R1&quot;&gt;R1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Current Status: Approved for C++23&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is my first ever proposal to WG21. It is a simple renaming proposal, proposing to fix an inconsistency in
the &lt;a href=&quot;https://wg21.link/P0323&quot;&gt;std::expected paper&lt;/a&gt;, such that &lt;code&gt;std::expected::value()&lt;/code&gt; returns the normal value
but &lt;code&gt;std::unexpected::value()&lt;/code&gt; actually returns the error (abnormal) value.&lt;/p&gt;
&lt;p&gt;History:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;2022-07-25: R1 approved for inclusion in C++23 in the 2022-07-25 WG21 plenary. (Plenary -&amp;gt; Approved)&lt;/li&gt;
&lt;li&gt;2022-07-08: R1 seen by LWG, approved for plenary. (Stage 3 -&amp;gt; Plenary)&lt;/li&gt;
&lt;li&gt;2022-06-22: R0 passed &lt;a href=&quot;https://wg21.link/P2575R0&quot;&gt;2022-05 LEWG Electronic Poll&lt;/a&gt;. (EP -&amp;gt; Stage 3)&lt;/li&gt;
&lt;li&gt;2022-06-20: &lt;a href=&quot;https://wg21.link/P2549R1&quot;&gt;P2549R1&lt;/a&gt; shipped in the &lt;a href=&quot;https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/#mailing2022-07&quot;&gt;2022-07 Mailing&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;2022-03-01: R0 seen by LEWG, approved for EP. (Stage 2 -&amp;gt; EP)&lt;/li&gt;
&lt;li&gt;2022-02-13: &lt;a href=&quot;https://wg21.link/P2549R0&quot;&gt;P2549R0&lt;/a&gt; shipped in the &lt;a href=&quot;https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/#mailing2022-02&quot;&gt;2022-02 Mailing&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;P2573: &lt;code&gt;= delete(&quot;should have a reason&quot;);&lt;/code&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Audience: CWG&lt;/li&gt;
&lt;li&gt;Target: C++26&lt;/li&gt;
&lt;li&gt;Revisions: &lt;a href=&quot;https://wg21.link/P2573R0&quot;&gt;R0&lt;/a&gt;, &lt;a href=&quot;https://wg21.link/P2573R1&quot;&gt;R1&lt;/a&gt;, &lt;a href=&quot;https://wg21.link/P2573R2&quot;&gt;R2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Current Status: Approved for C++26&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;C++20 added &lt;code&gt;[[nodiscard(&quot;with reason&quot;)]]&lt;/code&gt;, together with &lt;code&gt;[[deprecated]]&lt;/code&gt; and &lt;code&gt;static_assert&lt;/code&gt;, forming the group of &quot;diagnostic with reason&quot; constructs in C++.
&lt;code&gt;= delete&lt;/code&gt; functions often have a reason to delete themselves (alternative exist, prevent rvalue dangling, etc), so this proposal proposed adding a reason clause
to it too.&lt;/p&gt;
&lt;p&gt;History:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;2024-04-15: &lt;a href=&quot;https://wg21.link/P2573R2&quot;&gt;P2573R2&lt;/a&gt; shipped in the &lt;a href=&quot;https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/#mailing2024-04&quot;&gt;2024-04 post-Tokyo Mailing&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;2024-03-23: R2 approved for inclusion in C++26 in the Tokyo (2024-03) WG21 plenary. (Plenary -&amp;gt; Approved)&lt;/li&gt;
&lt;li&gt;2024-03-22: R2 seen by CWG in Tokyo (2024-03), approved for plenary. (Stage 3 -&amp;gt; Plenary)&lt;/li&gt;
&lt;li&gt;2024-03-19: R1 seen by EWG in Tokyo (2024-03), approved for CWG. (Stage 2 -&amp;gt; Stage 3)&lt;/li&gt;
&lt;li&gt;2023-12-15: &lt;a href=&quot;https://wg21.link/P2573R1&quot;&gt;P2573R1&lt;/a&gt; shipped in the &lt;a href=&quot;https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/#mailing2023-12&quot;&gt;2023-12 post-Kona Mailing&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;2023-11-06: Backtrack to Stage 1, then &lt;a href=&quot;https://wg21.link/D2573R1&quot;&gt;D2573R1&lt;/a&gt; seen by EWGI in Kona (2023-11), approved for EWG. (Stage 2 -&amp;gt; Stage 1 -&amp;gt; Stage 2)&lt;/li&gt;
&lt;li&gt;2022-04-12: &lt;a href=&quot;https://wg21.link/P2573R0&quot;&gt;P2573R0&lt;/a&gt; shipped in the &lt;a href=&quot;https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/#mailing2022-04&quot;&gt;2022-04 Mailing&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;P2613: Add the missing &lt;code&gt;empty()&lt;/code&gt; to &lt;code&gt;mdspan&lt;/code&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Audience: LEWG, LWG&lt;/li&gt;
&lt;li&gt;Target: C++23&lt;/li&gt;
&lt;li&gt;Revisions: &lt;a href=&quot;https://wg21.link/P2613R0&quot;&gt;R0&lt;/a&gt;, &lt;a href=&quot;https://wg21.link/P2613R1&quot;&gt;R1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Current Status: Approved for C++23&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is a very rushed paper. In mid-June, I spotted a bunch of problems with the
about-to-be-approved &lt;a href=&quot;https://wg21.link/P0009&quot;&gt;mdspan paper&lt;/a&gt;, and opened a &lt;a href=&quot;https://github.com/ORNL/cpp-proposals-pub/pull/262&quot;&gt;PR&lt;/a&gt;,
wanting to add &lt;code&gt;noexcept&lt;/code&gt; to some member functions, and also add the missing &lt;code&gt;mdspan::empty()&lt;/code&gt;.
In the following LWG small-group review session, &lt;code&gt;noexcept&lt;/code&gt; additions are approved, but a new function need its own proposal
to pass through LEWG review again. Thus the paper.&lt;/p&gt;
&lt;p&gt;History:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;2022-07-25: R1 approved for inclusion in C++23 in the 2022-07-25 WG21 plenary. (Plenary -&amp;gt; Approved)&lt;/li&gt;
&lt;li&gt;2022-07-23: R1 passed &lt;a href=&quot;https://wg21.link/P2611R0&quot;&gt;2022-07 LEWG Electronic Poll&lt;/a&gt;. (EP -&amp;gt; Plenary)&lt;/li&gt;
&lt;li&gt;2022-07-08: R1 seen by LWG, approved for plenary. (Stage 3 preapproval)&lt;/li&gt;
&lt;li&gt;2022-06-28: R0 seen by LEWG (of which I forgot to attend the telecon, sorry!), approved for EP. (Stage 2 -&amp;gt; EP)&lt;/li&gt;
&lt;li&gt;2022-06-25: &lt;a href=&quot;https://wg21.link/P2613R1&quot;&gt;P2613R1&lt;/a&gt; shipped in the &lt;a href=&quot;https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/#mailing2022-07&quot;&gt;2022-07 Mailing&lt;/a&gt; (LWG request a one-line wording change, so this is quick).&lt;/li&gt;
&lt;li&gt;2022-06-23: &lt;a href=&quot;https://wg21.link/P2613R0&quot;&gt;P2613R0&lt;/a&gt; shipped in the &lt;a href=&quot;https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/#mailing2022-06&quot;&gt;2022-06 Mailing&lt;/a&gt; (as a late paper).&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;P3351: &lt;code&gt;views::scan&lt;/code&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Audience: LEWG&lt;/li&gt;
&lt;li&gt;Target: C++29&lt;/li&gt;
&lt;li&gt;Revisions: &lt;a href=&quot;https://wg21.link/P3351R0&quot;&gt;R0&lt;/a&gt;, &lt;a href=&quot;https://wg21.link/P3351R1&quot;&gt;R1&lt;/a&gt;, &lt;a href=&quot;https://wg21.link/P3351R2&quot;&gt;R2&lt;/a&gt;, &lt;a href=&quot;https://wg21.link/P3351R3&quot;&gt;R3&lt;/a&gt;, &lt;a href=&quot;https://wg21.link/P3351R4&quot;&gt;R4&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Current Status: Stage 2&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This paper proposes the &lt;code&gt;views::scan&lt;/code&gt; range adaptor, which takes a range and a function that takes the current element and the current state as parameters. Basically, &lt;code&gt;views::scan&lt;/code&gt; is a lazy view version of &lt;code&gt;std::inclusive_scan&lt;/code&gt;, or &lt;code&gt;views::transform&lt;/code&gt; with a stateful function.&lt;/p&gt;
&lt;p&gt;History:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;2026-04-27: &lt;a href=&quot;https://wg21.link/P3351R4&quot;&gt;P3351R4&lt;/a&gt; shipped in the &lt;a href=&quot;https://open-std.org/jtc1/sc22/wg21/docs/papers/2026/#mailing2026-05&quot;&gt;2026-05 pre-Brno Mailing&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;2025-11-04: R3 seen by SG9 in Kona (2025-11), approved for LEWG. (Stage 1 -&amp;gt; Stage 2)&lt;/li&gt;
&lt;li&gt;2025-09-30: &lt;a href=&quot;https://wg21.link/P3351R3&quot;&gt;P3351R3&lt;/a&gt; shipped in the &lt;a href=&quot;https://open-std.org/jtc1/sc22/wg21/docs/papers/2025/#mailing2025-10&quot;&gt;2025-10 pre-Kona Mailing&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;2025-01-12: &lt;a href=&quot;https://wg21.link/P3351R2&quot;&gt;P3351R2&lt;/a&gt; shipped in the &lt;a href=&quot;https://open-std.org/jtc1/sc22/wg21/docs/papers/2025/#mailing2025-01&quot;&gt;2025-01 pre-Hagenberg Mailing&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;2024-09-24: &lt;a href=&quot;https://wg21.link/P3351R1&quot;&gt;P3351R1&lt;/a&gt; shipped in the &lt;a href=&quot;https://open-std.org/jtc1/sc22/wg21/docs/papers/2024/#mailing2024-10&quot;&gt;2024-10 pre-Wrocław Mailing&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;2024-07-08: &lt;a href=&quot;https://wg21.link/P3351R0&quot;&gt;P3351R0&lt;/a&gt; shipped in the &lt;a href=&quot;https://open-std.org/jtc1/sc22/wg21/docs/papers/2024/#mailing2024-07&quot;&gt;2024-07 post-St. Louis Mailing&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;P3423: Extending User-Generated Diagnostic Messages&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Audience: CWG&lt;/li&gt;
&lt;li&gt;Target: C++29&lt;/li&gt;
&lt;li&gt;Revisions: &lt;a href=&quot;https://wg21.link/P3423R0&quot;&gt;R0&lt;/a&gt;, &lt;a href=&quot;https://wg21.link/P3423R1&quot;&gt;R1&lt;/a&gt;, &lt;a href=&quot;https://wg21.link/P3423R2&quot;&gt;R2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Current Status: Stage 3&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;During Varna (2023-06), &lt;a href=&quot;https://wg21.link/P2741R3&quot;&gt;P2741R3&lt;/a&gt; had been adopted into the C++26 working draft, which gave &lt;code&gt;static_assert&lt;/code&gt; the ability to accept a user-generated string-like object as the message parameter. This extension allowed the user of &lt;code&gt;static_assert&lt;/code&gt; to provide a more precise error message in compile time, thus significantly increasing the user-friendliness of libraries. This proposal, therefore, proposes to unify the language by allowing other constructs in the language that currently accept a message parameter, namely &lt;code&gt;[[nodiscard]]&lt;/code&gt;, &lt;code&gt;[[deprecated]]&lt;/code&gt;, and &lt;code&gt;= delete&lt;/code&gt;, to also allow a user-generated string-like object as the provided message.&lt;/p&gt;
&lt;p&gt;History:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;2026-07-21: &lt;a href=&quot;https://wg21.link/P3423R2&quot;&gt;P3423R2&lt;/a&gt; shipped in the &lt;a href=&quot;https://open-std.org/jtc1/sc22/wg21/docs/papers/2026/#mailing2026-08&quot;&gt;2026-08 Mailing&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;2025-11-04: R1 seen by EWG in Kona (2025-11), approved for CWG. (Stage 2 -&amp;gt; Stage 3)&lt;/li&gt;
&lt;li&gt;2025-01-12: &lt;a href=&quot;https://wg21.link/P3423R1&quot;&gt;P3423R1&lt;/a&gt; shipped in the &lt;a href=&quot;https://open-std.org/jtc1/sc22/wg21/docs/papers/2025/#mailing2025-01&quot;&gt;2025-01 pre-Hagenberg Mailing&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;2024-11-22: R0 seen by EWGI in Wrocław (2024-11), approved for EWG. (Stage 1 -&amp;gt; Stage 2)&lt;/li&gt;
&lt;li&gt;2024-10-14: &lt;a href=&quot;https://wg21.link/P3423R0&quot;&gt;P3423R0&lt;/a&gt; shipped in the &lt;a href=&quot;https://open-std.org/jtc1/sc22/wg21/docs/papers/2024/#mailing2024-10&quot;&gt;2024-10 pre-Wrocław Mailing&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;P3831: Contract Labels Should Use Annotation Syntax&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Audience: SG21&lt;/li&gt;
&lt;li&gt;Target: &lt;a href=&quot;https://wg21.link/P3400R1&quot;&gt;P3400R1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Revisions: &lt;a href=&quot;https://wg21.link/P3831R0&quot;&gt;R0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Current Status: Rejected&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Contract labels (&lt;a href=&quot;https://wg21.link/P3400R1&quot;&gt;P3400R1&lt;/a&gt;) are one of the most important extensions proposed to C++26 Contracts, providing the ability to control the behavior of specific contract assertions. This proposal argues that instead of inventing a new syntax for parameterizing contract assertions, the labels should utilize the existing feature in the standard that permits this parameterization with defined semantics, namely annotations (&lt;a href=&quot;https://wg21.link/P3394R4&quot;&gt;P3394R4&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;History:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;2025-09-07: &lt;a href=&quot;https://wg21.link/P3831R0&quot;&gt;P3831R0&lt;/a&gt; shipped in the &lt;a href=&quot;https://open-std.org/jtc1/sc22/wg21/docs/papers/2025/#mailing2025-09&quot;&gt;2025-09 Mailing&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;2026-06-09: R0 seen by EWG in Brno (2026-06) and rejected. (Stage 1 -&amp;gt; Stage 2 -&amp;gt; Rejected)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;P4205: Range-Based Searchers&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Audience: SG9&lt;/li&gt;
&lt;li&gt;Target: C++29&lt;/li&gt;
&lt;li&gt;Revisions: &lt;a href=&quot;https://wg21.link/P4205R0&quot;&gt;R0&lt;/a&gt;, &lt;a href=&quot;https://wg21.link/P4205R1&quot;&gt;R1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Current Status: Stage 1&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This proposal introduces &lt;code&gt;std::ranges&lt;/code&gt; versions of the &lt;code&gt;Searcher&lt;/code&gt; overload of the &lt;code&gt;std::search&lt;/code&gt; algorithm, which takes a searcher object instead of an iterator pair and (optionally) a predicate.
It was originally introduced for more performant specialized searching.&lt;/p&gt;
&lt;p&gt;As customary in Ranges algorithms, this proposal also proposes Range-ified versions of the existing standard searchers, along with a concept &lt;code&gt;std::searchable&lt;/code&gt; for better capturing the semantic requirements of standard searchers.&lt;/p&gt;
&lt;p&gt;History:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;2026-07-21: &lt;a href=&quot;https://wg21.link/P4205R1&quot;&gt;P4205R1&lt;/a&gt; shipped in the &lt;a href=&quot;https://open-std.org/jtc1/sc22/wg21/docs/papers/2026/#mailing2026-08&quot;&gt;2026-08 Mailing&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;2026-04-29: &lt;a href=&quot;https://wg21.link/P4205R0&quot;&gt;P4205R0&lt;/a&gt; shipped in the &lt;a href=&quot;https://open-std.org/jtc1/sc22/wg21/docs/papers/2026/#mailing2026-05&quot;&gt;2026-05 pre-Brno Mailing&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;P4211: Adaptors For Closed Ranges&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Audience: SG9&lt;/li&gt;
&lt;li&gt;Target: C++29&lt;/li&gt;
&lt;li&gt;Revisions: &lt;a href=&quot;https://wg21.link/P4211R0&quot;&gt;R0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Current Status: Stage 1&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This proposal introduces a family of adaptors that convert closed ranges into half-open ranges, as expected by most other standard library facilities in C++, thus providing direct support for a range model that has been fundamentally incompatible with the C++ iterator model until now.&lt;/p&gt;
&lt;p&gt;History:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;2026-05-05: &lt;a href=&quot;https://wg21.link/P4211R0&quot;&gt;P4211R0&lt;/a&gt; shipped in the &lt;a href=&quot;https://open-std.org/jtc1/sc22/wg21/docs/papers/2026/#mailing2026-05&quot;&gt;2026-05 pre-Brno Mailing&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Hello World</title><link>https://mick235711.github.io/2018/08/22/hello-world/</link><guid isPermaLink="true">https://mick235711.github.io/2018/08/22/hello-world/</guid><description>The first post on Mick235711&apos;s personal website.</description><pubDate>Wed, 22 Aug 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;(Automatically generated by the &lt;a href=&quot;https://github.com/daviddarnes/alembic&quot;&gt;Alembic&lt;/a&gt; theme)&lt;/p&gt;
&lt;p&gt;This is my very first blog post. I haven&apos;t written anything yet but I&apos;m sure I have some great stories to tell.&lt;/p&gt;
</content:encoded></item></channel></rss>