diff --git a/riegeli/base/initializer.h b/riegeli/base/initializer.h index a0faddfe..1a46277d 100644 --- a/riegeli/base/initializer.h +++ b/riegeli/base/initializer.h @@ -217,7 +217,8 @@ class InitializerBase { const T& ConstReference(TemporaryStorage&& storage ABSL_ATTRIBUTE_LIFETIME_BOUND = TemporaryStorage()) && { - return methods()->const_reference(context(), std::move(storage)); + T&& reference = methods()->const_reference(context(), std::move(storage)); + return reference; } private: @@ -265,25 +266,27 @@ class InitializerBase { static T&& ReferenceMethodFromConstMaker(TypeErasedRef context, TemporaryStorage&& storage); - static const T& ConstReferenceMethodDefault(TypeErasedRef context, - TemporaryStorage&& storage); - - template ::value, int> = 0> - static const T& ConstReferenceMethodFromObject(TypeErasedRef context, - TemporaryStorage&& storage); - template ::value, int> = 0> - static const T& ConstReferenceMethodFromObject(TypeErasedRef context, - TemporaryStorage&& storage); + // This is used only if `!CanBindTo::value` and + // `CanBindTo::value`. Otherwise + // `ConstReferenceMethodFromObject` reuses `ReferenceMethodFromObject`. + template + static T&& ConstReferenceMethodFromObject(TypeErasedRef context, + TemporaryStorage&& storage); + // This is used only if `!CanBindTo::value` and + // `CanBindTo::value`. Otherwise + // `ConstReferenceMethodFromMaker` reuses `ReferenceMethodFromMaker`. template - static const T& ConstReferenceMethodFromMaker(TypeErasedRef context, - TemporaryStorage&& storage); + static T&& ConstReferenceMethodFromMaker(TypeErasedRef context, + TemporaryStorage&& storage); + // This is used only if `!CanBindTo::value` and + // `CanBindTo::value`. Otherwise + // `ConstReferenceMethodFromConstMaker` reuses + // `ReferenceMethodFromConstMaker`. template - static const T& ConstReferenceMethodFromConstMaker( - TypeErasedRef context, TemporaryStorage&& storage); + static T&& ConstReferenceMethodFromConstMaker(TypeErasedRef context, + TemporaryStorage&& storage); protected: struct Methods { @@ -293,8 +296,10 @@ class InitializerBase { void (*construct_at)(TypeErasedRef context, void* ptr); #endif T && (*reference)(TypeErasedRef context, TemporaryStorage&& storage); - const T& (*const_reference)(TypeErasedRef context, - TemporaryStorage&& storage); + // `const_reference` returns `const T&` cast to `T&&`, so that its + // implementations can reuse code from `reference` if possible. + T && (*const_reference)(TypeErasedRef context, + TemporaryStorage&& storage); }; explicit InitializerBase(const Methods* methods); @@ -312,7 +317,27 @@ class InitializerBase { #else ConstructAtMethodDefault, #endif - ReferenceMethodDefault, ConstReferenceMethodDefault}; + ReferenceMethodDefault, ReferenceMethodDefault}; + + template < + typename Arg, + std::enable_if_t>, + CanBindTo>::value, + int> = 0> + static constexpr auto MakeConstReferenceMethodFromObject() + -> T && (*)(TypeErasedRef context, TemporaryStorage&& storage) { + return ConstReferenceMethodFromObject; + } + template < + typename Arg, + std::enable_if_t< + absl::disjunction, + absl::negation>>::value, + int> = 0> + static constexpr auto MakeConstReferenceMethodFromObject() + -> T && (*)(TypeErasedRef context, TemporaryStorage&& storage) { + return ReferenceMethodFromObject; + } template static constexpr Methods kMethodsFromObject = { @@ -321,7 +346,28 @@ class InitializerBase { #else ConstructAtMethodFromObject, #endif - ReferenceMethodFromObject, ConstReferenceMethodFromObject}; + ReferenceMethodFromObject, + MakeConstReferenceMethodFromObject()}; + + template >, + CanBindTo>::value, + int> = 0> + static constexpr auto MakeConstReferenceMethodFromMaker() + -> T && (*)(TypeErasedRef context, TemporaryStorage&& storage) { + return ConstReferenceMethodFromMaker; + } + template , + absl::negation>>::value, + int> = 0> + static constexpr auto MakeConstReferenceMethodFromMaker() + -> T && (*)(TypeErasedRef context, TemporaryStorage&& storage) { + return ReferenceMethodFromMaker; + } template static constexpr Methods kMethodsFromMaker = { @@ -331,7 +377,27 @@ class InitializerBase { ConstructAtMethodFromMaker, #endif ReferenceMethodFromMaker, - ConstReferenceMethodFromMaker}; + MakeConstReferenceMethodFromMaker()}; + + template >, + CanBindTo>::value, + int> = 0> + static constexpr auto MakeConstReferenceMethodFromConstMaker() + -> T && (*)(TypeErasedRef context, TemporaryStorage&& storage) { + return ConstReferenceMethodFromConstMaker; + } + template , + absl::negation>>::value, + int> = 0> + static constexpr auto MakeConstReferenceMethodFromConstMaker() + -> T && (*)(TypeErasedRef context, TemporaryStorage&& storage) { + return ReferenceMethodFromConstMaker; + } template static constexpr Methods kMethodsFromConstMaker = { @@ -341,7 +407,7 @@ class InitializerBase { ConstructAtMethodFromConstMaker, #endif ReferenceMethodFromConstMaker, - ConstReferenceMethodFromConstMaker}; + MakeConstReferenceMethodFromConstMaker()}; const Methods* methods() const { return methods_; } TypeErasedRef context() const { return context_; } @@ -998,43 +1064,32 @@ T&& InitializerBase::ReferenceMethodFromConstMaker( } template -const T& InitializerBase::ConstReferenceMethodDefault( - ABSL_ATTRIBUTE_UNUSED TypeErasedRef context, - TemporaryStorage&& storage) { - return storage.emplace(); -} - -template -template ::value, int>> -const T& InitializerBase::ConstReferenceMethodFromObject( +template +T&& InitializerBase::ConstReferenceMethodFromObject( TypeErasedRef context, ABSL_ATTRIBUTE_UNUSED TemporaryStorage&& storage) { - return context.Cast(); -} - -template -template ::value, int>> -const T& InitializerBase::ConstReferenceMethodFromObject( - TypeErasedRef context, TemporaryStorage&& storage) { - return storage.emplace(context.Cast()); + const T& reference = context.Cast(); + return const_cast(reference); } template template -const T& InitializerBase::ConstReferenceMethodFromMaker( +T&& InitializerBase::ConstReferenceMethodFromMaker( TypeErasedRef context, TemporaryStorage&& storage) { - return context.Cast>().template ConstReference( - std::move(storage)); + const T& reference = + context.Cast>().template ConstReference( + std::move(storage)); + return const_cast(reference); } template template -const T& InitializerBase::ConstReferenceMethodFromConstMaker( +T&& InitializerBase::ConstReferenceMethodFromConstMaker( TypeErasedRef context, TemporaryStorage&& storage) { - return context.Cast&>().template ConstReference( - std::move(storage)); + const T& reference = + context.Cast&>().template ConstReference( + std::move(storage)); + return const_cast(reference); } #if !__cpp_guaranteed_copy_elision