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 11:10:29 +0300
On 24 Apr 2025 10:49, Andrey Semashev wrote:
> 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);
> }

Actually, it should be noexcept and constexpr, if possible:

  template<SupportsMovedFrom T>
  [[nodiscard]] constexpr T make_moved_from()
    noexcept(is_nothrow_constructible_v< T, moved_from_t >)
  {
    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 08:10:32