Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Should we encourage deriving from quantity and quantity_point? #680

Open
mpusz opened this issue Feb 14, 2025 · 3 comments
Open

Should we encourage deriving from quantity and quantity_point? #680

mpusz opened this issue Feb 14, 2025 · 3 comments
Labels
design Design-related discussion iso The ISO C++ Committee related work refactor Modify existing code potentially causing breaking changes

Comments

@mpusz
Copy link
Owner

mpusz commented Feb 14, 2025

A long time ago, one of the contributors changed the Quantity concept to allow also classes derived from the quantity class template. At this point, I thought this idea was OK. However, as C++ standardization approaches fast, we may have to reconsider this decision.

The problem with concept-based programming is that concepts do not allow implicit conversion for function arguments. So the following two function templates are not equivalent:

template<auto R, typename Rep>
void foo(const quantity<R, Rep>& q);

template<Quantity Q>
void boo(const Q& q);

C++ library never encouraged deriving from its own class templates, but all operators worked fine if someone did, thanks to the foo approach.

In mp-units, in some places, we use boo approach. Directly or indirectly, we require a template dependent name to satisfy Quantity or QuantityPoint concepts. This would prevent the derived classes from working if those concepts were refactored to accept the type explicitly.

On the other hand, explicitly stating that a Quantity is also everything derived from quantity may be controversial.

We should consider changing Quantity and QuantityPoint concepts to do explicit match and carefully refactor the cases where it is used incorrectly.

@mpusz mpusz added design Design-related discussion iso The ISO C++ Committee related work refactor Modify existing code potentially causing breaking changes labels Feb 14, 2025
@chiphogg
Copy link
Collaborator

I assume we want to disallow inheriting from quantity and quantity_point. Seems like a pretty suspect operation.

Do you (or does anyone else) have a concrete use case where deriving from quantity or quantity_point makes good sense?

@mpusz
Copy link
Owner Author

mpusz commented Feb 17, 2025

@chiphogg, my understanding is that some users derive from quantity to add more information to it. Here is an example:

template<Reference auto R, basic_fixed_string additional_nttp_argument,
RepresentationOf<get_quantity_spec(R)> Rep = double>
struct child_quantity : quantity<R, Rep> {
using quantity_type = quantity<R, Rep>;
static constexpr auto reference = R;
static constexpr auto quantity_spec = quantity_type::quantity_spec;
static constexpr auto dimension = quantity_type::dimension;
static constexpr auto unit = quantity_type::unit;
using rep = Rep;
child_quantity() = default;
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
constexpr explicit(false) child_quantity(const quantity_type& t) : quantity_type(t) {}
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
constexpr explicit(false) child_quantity(quantity_type&& t) : quantity_type(std::move(t)) {}
constexpr child_quantity& operator=(const quantity_type& q)
{
quantity_type::operator=(q);
return *this;
}
constexpr child_quantity& operator=(quantity_type&& q)
{
quantity_type::operator=(std::move(q));
return *this;
}
// NOLINTBEGIN(google-explicit-constructor, hicpp-explicit-conversions)
constexpr explicit(false) operator quantity_type&() & noexcept { return *this; }
constexpr explicit(false) operator const quantity_type&() const& noexcept { return *this; }
constexpr explicit(false) operator quantity_type&&() && noexcept { return *this; }
constexpr explicit(false) operator const quantity_type&&() const&& noexcept { return *this; }
// NOLINTEND(google-explicit-constructor, hicpp-explicit-conversions)
};
.

@chiphogg
Copy link
Collaborator

That looks incredibly complex and error prone! I also couldn't tell what new information was actually being added, or whether this is all just boilerplate.

My instinct is still to lean on standard guidance to "prefer composition to inheritance". I'm wary of having other types that purport to model the "is-a" relationship with quantity. If you (or anyone else!) have some examples of real concrete use cases that require inheriting from quantity, I'd be interested to see them, but this one just seems like a placeholder to prove that inheritance is possible.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
design Design-related discussion iso The ISO C++ Committee related work refactor Modify existing code potentially causing breaking changes
Projects
None yet
Development

No branches or pull requests

2 participants