C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Idea: moved_from<T>() for Efficient Moved-From State Construction

From: Andrey Semashev <andrey.semashev_at_[hidden]>
Date: Thu, 24 Apr 2025 10:49:04 +0300
On 24 Apr 2025 04:09, Elazar via Std-Proposals wrote:
> Hey everyone,
>
> I've been working on a new proposal for the C++ Standard Library that
> I'd like to share: "A Library-Based Mechanism for Moved-From
> Initialization (moved_from())"
>
> *Problem:* Creating an object just to immediately overwrite it, like:
>
> BigExpensiveType obj; // Default construction we don't need
> obj = get_actual_value(); // Immediate overwrite
>
> This is especially wasteful for types with non-trivial constructors.
> Current workarounds like placement new or custom factory functions are
> either unsafe or non-standard.
>
> *Proposal:* Add a standardized |moved_from<T>()| function that
> constructs objects directly in a moved-from state. Type authors opt-in
> by implementing |T::moved_from()|.
>
> BigExpensiveType obj = moved_from<BigExpensiveType>();
> obj = get_actual_value(); // Now we're just overwriting a moved-from
> state
>
> This shifts implementation responsibility from client code to the type
> author (who knows the most about safely initializing their type), while
> providing stronger guarantees for compiler optimizations.
>
> The full proposal is available here: https://gist.github.com/elazarg/
> ffe096f8e5c237d60a5d6f5205f50e2e <https://gist.github.com/elazarg/
> ffe096f8e5c237d60a5d6f5205f50e2e>
>
> What do you think? Are there specific use cases you'd like to see
> addressed? Any implementation concerns?

I think, I'd rather prefer an approach where there is a tag argument
that can be passed to the T's constructor.

  namespace std {
  struct moved_from_t {};
  inline constexpr moved_from_t moved_from;

  template<typename T>
  concept SupportsMovedFrom =
    is_constructible_v< T, moved_from_t >;

  template<SupportsMovedFrom T>
  [[nodiscard]] T make_moved_from() {
    return T(moved_from);
  }
  }

  struct LargeBuffer {
    // ...

    LargeBuffer() = default;
    LargeBuffer(std::moved_from_t) {}
  };

First, this avoids false positives, when the user's T already provides
T::moved_from(), possibly with a different meaning.

Second, using a constructor to directly initialize an object seems more
intuitive and direct than a factory function. And in most cases, the
user would need such a constructor anyway in order to implement
T::moved_from() efficiently, as you note yourself in the proposal. So:

  LargeBuffer buf(std::moved_from);

is better than

  LargeBuffer buf = std::moved_from< LargeBuffer >();

at least because it is avoids code duplication and likely more efficient.

You still retain the ability to create the moved-from object using the
make_moved_from helper function (note that this name follows the
existing convention of make_unique, make_pair, make_tuple, etc.) So, if
needed, this is still possible:

  foo(std::make_moved_from< LargeBuffer >());

Received on 2025-04-24 07:49:07