Builder pattern that forces all members to be initialized at compile-time
Would such a pattern actually be useful in practice?


Not really in my opinion. In C++20 you can use designated initializer lists like {.min = 10, .max = 20} and I mean if the argument is not clear from the context than there are various ways to improve this, even today.


A comment. Foo f(/*min=*/10);
User-defined literals Rectangle r(10_width, 200_height);
Strong types: Player p(Position(10, 10));
Variables: const int size = 10; Square s(size);


This also doesn't work with non-copyable, non-moveable types, although those are rare in practice. The members also need to be default constructible unfortunately.

Some thoughts:


Note that your code has undefined behavior, since two of the variables are not set on the first call.
The build function is required in Java, but in C++ you can use a conversion operator. Although this suffers from the same problem that expression templates have: Using type deduction will not get you a Foo.
Delete copy construction for the builder object, since there is no reason to copy it.
Don't use 0 and 1 for bools please; false and true is clearer IMO.
Consider using noexcept and constexpr (if applicable).



  Is there an elegant way of sharing the member variables of the Builder objects, despite the results of each setter call being of a different type? (For cheap-to-move types, I am thinking along the lines of a "data block"-like struct, that gets moved from this into the result.)


Yes that's a good idea. Here's my take on it:

template &lt;bool x_set = false, bool y_set = false, bool z_set = false&gt;
struct FooBuilder {
  constexpr FooBuilder() noexcept = default;
  FooBuilder(const FooBuilder &amp;) = delete;
  FooBuilder &amp;operator=(const FooBuilder &amp;) = delete;

  constexpr auto x(int val) noexcept {
    members_.x_ = val;
    return FooBuilder&lt;true, y_set, z_set&gt;(std::move(*this));
  }
  constexpr auto y(int val) noexcept {
    members_.y_ = val;
    return FooBuilder&lt;x_set, true, z_set&gt;(std::move(*this));
  }
  constexpr auto z(int val) noexcept {
    static_assert(!z_set, "cannot set z twice!");
    members_.z_ = val;
    return FooBuilder&lt;x_set, y_set, true&gt;(std::move(*this));
  }

  constexpr operator Foo() noexcept {
    static_assert(x_set &amp;&amp; y_set &amp;&amp; z_set, "all members must be set");
    return {members_.x_, members_.y_, members_.z_};
  }
  constexpr Foo build() noexcept { return *this; }

private:
  template &lt;bool... args&gt;
  constexpr FooBuilder(FooBuilder&lt;args...&gt; &amp;&amp;other) noexcept
      : members_{std::move(other.members_.x_), std::move(other.members_.y_),
                 std::move(other.members_.z_)} {}

  template &lt;bool, bool, bool&gt; friend struct FooBuilder;

  struct Proxy {
    int x_, y_, z_;
  } members_{};
};


There is still a lot of boilerplate, but unfortunately it cannot be removed since C++ doesn't have reflection yet.