@@ -110,16 +110,16 @@ struct model_ptr
110110 model_ptr& operator =(model_ptr&&) = default ;
111111
112112 template <typename OtherT>
113- model_ptr (model_ptr<OtherT> const & other) : data_(other.data_ ) {} // NOLINT
113+ model_ptr (model_ptr<OtherT> const & other) : data_(makeCompatibleCopy( other) ) {} // NOLINT
114114
115115 template <typename OtherT>
116- model_ptr (model_ptr<OtherT>&& other) : data_(std::move(other.data_ )) {} // NOLINT
116+ model_ptr (model_ptr<OtherT>&& other) : data_(makeCompatibleMove( std::move(other) )) {} // NOLINT
117117
118118 template <typename OtherT>
119- model_ptr& operator = (model_ptr<OtherT> const & other) {data_ = other. data_ ; return *this ;}
119+ model_ptr& operator = (model_ptr<OtherT> const & other) {data_ = makeCompatibleCopy ( other) ; return *this ;}
120120
121121 template <typename OtherT>
122- model_ptr& operator = (model_ptr<OtherT>&& other) {data_ = std::move (other. data_ ); return *this ;}
122+ model_ptr& operator = (model_ptr<OtherT>&& other) {data_ = makeCompatibleMove ( std::move (other) ); return *this ;}
123123
124124 template <typename ... Args>
125125 explicit model_ptr (std::in_place_t , Args&&... args)
@@ -156,6 +156,44 @@ struct model_ptr
156156 inline operator bool () const {return data_.addr_ ;} // NOLINT (allow implicit bool cast)
157157
158158private:
159+ template <typename >
160+ static constexpr bool AlwaysFalse = false ;
161+
162+ template <typename OtherT>
163+ [[nodiscard]] static T makeCompatibleCopy (model_ptr<OtherT> const & other)
164+ {
165+ if constexpr (std::is_base_of_v<T, OtherT> && !std::is_same_v<T, OtherT>) {
166+ // Avoid slicing on derived->base conversions by rebuilding from node identity.
167+ if constexpr (requires { T (other.data_ .model_ , other.data_ .addr_ , other.data_ .data_ , detail::mp_key{0 }); }) {
168+ return T (other.data_ .model_ , other.data_ .addr_ , other.data_ .data_ , detail::mp_key{0 });
169+ } else if constexpr (requires { T (other.data_ .model_ , other.data_ .addr_ , detail::mp_key{0 }); }) {
170+ return T (other.data_ .model_ , other.data_ .addr_ , detail::mp_key{0 });
171+ } else {
172+ static_assert (
173+ AlwaysFalse<OtherT>,
174+ " Derived-to-base model_ptr conversion requires a constructor from model/address." );
175+ }
176+ }
177+ return T (other.data_ );
178+ }
179+
180+ template <typename OtherT>
181+ [[nodiscard]] static T makeCompatibleMove (model_ptr<OtherT>&& other)
182+ {
183+ if constexpr (std::is_base_of_v<T, OtherT> && !std::is_same_v<T, OtherT>) {
184+ if constexpr (requires { T (std::move (other.data_ .model_ ), other.data_ .addr_ , std::move (other.data_ .data_ ), detail::mp_key{0 }); }) {
185+ return T (std::move (other.data_ .model_ ), other.data_ .addr_ , std::move (other.data_ .data_ ), detail::mp_key{0 });
186+ } else if constexpr (requires { T (std::move (other.data_ .model_ ), other.data_ .addr_ , detail::mp_key{0 }); }) {
187+ return T (std::move (other.data_ .model_ ), other.data_ .addr_ , detail::mp_key{0 });
188+ } else {
189+ static_assert (
190+ AlwaysFalse<OtherT>,
191+ " Derived-to-base model_ptr conversion requires a constructor from model/address." );
192+ }
193+ }
194+ return T (std::move (other.data_ ));
195+ }
196+
159197 T data_;
160198};
161199
0 commit comments