diff --git a/riegeli/base/initializer.h b/riegeli/base/initializer.h index a794f6e6..eb5c4540 100644 --- a/riegeli/base/initializer.h +++ b/riegeli/base/initializer.h @@ -67,8 +67,8 @@ class InitializerBase { // It is preferred to explicitly call `Construct()` instead. This conversion // allows to pass `Initializer` to another function which accepts a value // convertible to `T` for construction in-place, including functions like - // `std::vector::emplace_back()` or the constructor of `absl::optional` - // or `absl::StatusOr`. + // `std::make_unique()`, `std::vector::emplace_back()`, or the + // constructor of `absl::optional` or `absl::StatusOr`. /*implicit*/ operator T() && { return std::move(*this).Construct(); } private: @@ -126,7 +126,11 @@ class InitializerValueBase : public InitializerBase { using ReferenceStorage = initializer_internal::ReferenceStorage; // Constructs the `T`, or returns a reference to an already constructed object - // if that was passed to the `Initializer`. This can avoid moving it. + // if that was passed to the `Initializer`. + // + // `Reference()` instead of `Construct()` can avoid moving the object if the + // caller does not need to store the object, or if it will be moved later + // because the target location for the object is not ready yet. // // `reference_storage` must outlive usages of the returned reference. T&& Reference(ReferenceStorage&& reference_storage @@ -134,6 +138,20 @@ class InitializerValueBase : public InitializerBase { return methods()->reference(this->context(), std::move(reference_storage)); } + // Constructs the `T`, or returns a const reference to an already constructed + // object if that was passed to the `Initializer`. + // + // `ConstReference()` can avoid moving the object in more cases than + // `Reference()` if the caller does not need to store the object. + // + // `reference_storage` must outlive usages of the returned reference. + const T& ConstReference(ReferenceStorage&& reference_storage + ABSL_ATTRIBUTE_LIFETIME_BOUND = + ReferenceStorage()) && { + return methods()->const_reference(this->context(), + std::move(reference_storage)); + } + private: static T&& ReferenceMethodDefault(void* context, ReferenceStorage&& reference_storage); @@ -157,33 +175,57 @@ class InitializerValueBase : public InitializerBase { static T&& ReferenceMethodFromConstMaker( void* context, ReferenceStorage&& reference_storage); + static const T& ConstReferenceMethodDefault( + void* context, ReferenceStorage&& reference_storage); + + template ::value, int> = 0> + static const T& ConstReferenceMethodFromObject( + void* context, ReferenceStorage&& reference_storage); + template ::value, int> = 0> + static const T& ConstReferenceMethodFromObject( + void* context, ReferenceStorage&& reference_storage); + + template + static const T& ConstReferenceMethodFromMaker( + void* context, ReferenceStorage&& reference_storage); + + template + static const T& ConstReferenceMethodFromConstMaker( + void* context, ReferenceStorage&& reference_storage); + protected: struct Methods : InitializerValueBase::InitializerBase::Methods { T && (*reference)(void* context, ReferenceStorage&& reference_storage); + const T& (*const_reference)(void* context, + ReferenceStorage&& reference_storage); }; #if __cpp_aggregate_bases template static constexpr Methods kMethodsDefault = { InitializerValueBase::InitializerBase::template kMethodsDefault<>, - ReferenceMethodDefault}; + ReferenceMethodDefault, ConstReferenceMethodDefault}; template static constexpr Methods kMethodsFromObject = { InitializerValueBase::InitializerBase::template kMethodsFromObject, - ReferenceMethodFromObject}; + ReferenceMethodFromObject, ConstReferenceMethodFromObject}; template static constexpr Methods kMethodsFromMaker = { InitializerValueBase::InitializerBase::template kMethodsFromMaker< Args...>, - ReferenceMethodFromMaker}; + ReferenceMethodFromMaker, + ConstReferenceMethodFromMaker}; template static constexpr Methods kMethodsFromConstMaker = { InitializerValueBase::InitializerBase::template kMethodsFromConstMaker< Args...>, - ReferenceMethodFromConstMaker}; + ReferenceMethodFromConstMaker, + ConstReferenceMethodFromConstMaker}; #else static constexpr Methods MakeMethodsDefault() { Methods methods; @@ -191,6 +233,7 @@ class InitializerValueBase : public InitializerBase { methods) = InitializerValueBase::InitializerBase::template kMethodsDefault<>; methods.reference = ReferenceMethodDefault; + methods.const_reference = ConstReferenceMethodDefault; return methods; } template @@ -203,6 +246,7 @@ class InitializerValueBase : public InitializerBase { methods) = InitializerValueBase::InitializerBase::template kMethodsFromObject; methods.reference = ReferenceMethodFromObject; + methods.const_reference = ConstReferenceMethodFromObject; return methods; } template @@ -216,6 +260,7 @@ class InitializerValueBase : public InitializerBase { InitializerValueBase::InitializerBase::template kMethodsFromMaker< Args...>; methods.reference = ReferenceMethodFromMaker; + methods.const_reference = ConstReferenceMethodFromMaker; return methods; } template @@ -229,11 +274,12 @@ class InitializerValueBase : public InitializerBase { InitializerValueBase::InitializerBase::template kMethodsFromConstMaker< Args...>; methods.reference = ReferenceMethodFromConstMaker; + methods.const_reference = ConstReferenceMethodFromConstMaker; return methods; } template static constexpr Methods kMethodsFromConstMaker = - MakeMethodsfromConstMaker(); + MakeMethodsFromConstMaker(); #endif explicit InitializerValueBase(const Methods* methods) @@ -649,15 +695,23 @@ class Initializer(reference); + } }; // `InitializerTarget::type` and `InitializerTargetT` deduce the @@ -788,6 +842,48 @@ T&& InitializerValueBase::ReferenceMethodFromConstMaker( std::move(reference_storage)); } +template +const T& InitializerValueBase::ConstReferenceMethodDefault( + ABSL_ATTRIBUTE_UNUSED void* context, ReferenceStorage&& reference_storage) { + reference_storage.emplace(); + return *reference_storage; +} + +template +template ::value, int>> +const T& InitializerValueBase::ConstReferenceMethodFromObject( + void* context, ABSL_ATTRIBUTE_UNUSED ReferenceStorage&& reference_storage) { + return std::forward( + *static_cast*>(context)); +} + +template +template ::value, int>> +const T& InitializerValueBase::ConstReferenceMethodFromObject( + void* context, ReferenceStorage&& reference_storage) { + reference_storage.emplace( + std::forward(*static_cast*>(context))); + return *reference_storage; +} + +template +template +const T& InitializerValueBase::ConstReferenceMethodFromMaker( + void* context, ReferenceStorage&& reference_storage) { + return std::move(*static_cast*>(context)) + .template ConstReference(std::move(reference_storage)); +} + +template +template +const T& InitializerValueBase::ConstReferenceMethodFromConstMaker( + void* context, ReferenceStorage&& reference_storage) { + return static_cast*>(context) + ->template ConstReference(std::move(reference_storage)); +} + template void InitializerAssignableValueBase::AssignToMethodDefault( ABSL_ATTRIBUTE_UNUSED void* context, T& object) { diff --git a/riegeli/base/initializer_internal.h b/riegeli/base/initializer_internal.h index ecbd94dc..9002a64b 100644 --- a/riegeli/base/initializer_internal.h +++ b/riegeli/base/initializer_internal.h @@ -26,7 +26,10 @@ namespace initializer_internal { // Internal storage which is conditionally needed for storing the object that // `MakerType::Reference()`, -// `MakerTypeFor::Reference()`, and `Initializer::Reference()` +// `MakerType::ConstReference()`, +// `MakerTypeFor::Reference()`, +// `MakerTypeFor::ConstReference()`, +// `Initializer::Reference()`, and `Initializer::ConstReference()` // refer to. // // The public name of this type is `MakerType::ReferenceStorage`, @@ -68,6 +71,12 @@ class ReferenceStorage { "not initialized"; return std::move(value_); } + const T& operator*() const& { + RIEGELI_ASSERT(initialized_) + << "Failed precondition of ReferenceStorage::operator*: " + "not initialized"; + return value_; + } private: union { @@ -93,6 +102,7 @@ class ReferenceStorage< } T&& operator*() && { return std::move(value_); } + const T& operator*() const& { return value_; } private: union { diff --git a/riegeli/base/maker.h b/riegeli/base/maker.h index 507c988c..5e3a568a 100644 --- a/riegeli/base/maker.h +++ b/riegeli/base/maker.h @@ -77,7 +77,11 @@ class MakerType : public ConditionallyAssignabletemplate ReferenceImpl(std::move(reference_storage)); } + // Constructs the `T`, or returns a const reference to an already constructed + // object if that was passed to the `MakerType`. + // + // `ConstReference()` can avoid moving the object in more cases than + // `Reference()` if the caller does not need to store the object. + // + // `reference_storage` must outlive usages of the returned reference. + template < + typename T, + std::enable_if_t::value, int> = 0> + const T& ConstReference(ReferenceStorage&& reference_storage + ABSL_ATTRIBUTE_LIFETIME_BOUND = + ReferenceStorage()) && { + return std::move(*this).template ConstReferenceImpl( + std::move(reference_storage)); + } + template ::value, + int> = 0> + const T& ConstReference(ReferenceStorage&& reference_storage + ABSL_ATTRIBUTE_LIFETIME_BOUND = + ReferenceStorage()) const& { + return this->template ConstReferenceImpl(std::move(reference_storage)); + } + // Makes `object` equivalent to the constructed `T`. This avoids constructing // a temporary `T` and moving from it. template ::value, int> = 0> + const T& ConstReferenceImpl( + ABSL_ATTRIBUTE_UNUSED ReferenceStorage&& reference_storage = + ReferenceStorage()) && { + return std::get<0>(std::move(args_)); + } + template ::value, + int> = 0> + const T& ConstReferenceImpl(ReferenceStorage&& reference_storage + ABSL_ATTRIBUTE_LIFETIME_BOUND = + ReferenceStorage()) && { + absl::apply( + [&](Args&&... args) { + reference_storage.emplace(std::forward(args)...); + }, + std::move(args_)); + return *reference_storage; + } + + template ::value, + int> = 0> + const T& ConstReferenceImpl( + ABSL_ATTRIBUTE_UNUSED ReferenceStorage&& reference_storage = + ReferenceStorage()) const& { + return std::get<0>(args_); + } + template ::value, + int> = 0> + const T& ConstReferenceImpl(ReferenceStorage&& reference_storage + ABSL_ATTRIBUTE_LIFETIME_BOUND = + ReferenceStorage()) const& { + absl::apply( + [&](const Args&... args) { reference_storage.emplace(args...); }, + args_); + return *reference_storage; + } + std::tuple args_; }; @@ -226,13 +298,17 @@ class MakerTypeFor : public ConditionallyAssignable` to another function which accepts // a value convertible to `T` for construction in-place, including functions - // like `std::vector::emplace_back()` or the constructor of - // `absl::optional` or `absl::StatusOr`. + // like `std::make_unique()`, `std::vector::emplace_back()`, or the + // constructor of `absl::optional` or `absl::StatusOr`. /*implicit*/ operator T() && { return std::move(*this).Construct(); } /*implicit*/ operator T() const& { return Construct(); } // Constructs the `T`, or returns a reference to an already constructed object - // if that was passed to the `MakerTypeFor`. This can avoid moving it. + // if that was passed to the `MakerTypeFor`. + // + // `Reference()` instead of `Construct()` can avoid moving the object if the + // caller does not need to store the object, or if it will be moved later + // because the target location for the object is not ready yet. // // `reference_storage` must outlive usages of the returned reference. T&& Reference(ReferenceStorage&& reference_storage @@ -249,6 +325,29 @@ class MakerTypeFor : public ConditionallyAssignable(std::move(reference_storage)); } + // Constructs the `T`, or returns a const reference to an already constructed + // object if that was passed to the `MakerTypeFor`. + // + // `ConstReference()` can avoid moving the object in more cases than + // `Reference()` if the caller does not need to store the object. + // + // `reference_storage` must outlive usages of the returned reference. + const T& ConstReference(ReferenceStorage&& reference_storage + ABSL_ATTRIBUTE_LIFETIME_BOUND = + ReferenceStorage()) && { + return std::move(maker_).template ConstReference( + std::move(reference_storage)); + } + template < + typename DependentT = T, + std::enable_if_t::value, + int> = 0> + const T& ConstReference(ReferenceStorage&& reference_storage + ABSL_ATTRIBUTE_LIFETIME_BOUND = + ReferenceStorage()) const& { + return maker_.template ConstReference(std::move(reference_storage)); + } + // Makes `object` equivalent to the constructed `T`. This avoids constructing // a temporary `T` and moving from it. template