I want to provide to my Rational
type a generic constructor that accepts a couple of generic arguments (constrained by a concept), so I can avoid to write dozens of different constructors for Natural
, Integer
, Real
, Irrational
, Complex
and its possible different combinations.
So I wrote something like this:
template<Numerical L, Numerical R>
[[nodiscard]] constexpr Rational(const L&& numerator, const R&& denominator) noexcept
: _numerator(static_cast<Integer>(std::forward<L>(numerator))),
_denominator(static_cast<Integer>(std::forward<R>(denominator))) {}
Notes:
_numerator
and_denominator
areInteger
types, which is an strong type forint
- It took me a while to figure out why I shouldn’t be taking rvalue references by const.
So, I remove the const and after spot that there’s some places in my code where I was using lvalue (const) references (so I had to add the qualifier removers type traits provided by the standard) everything works. But now, I don’t know what should be the correct approach for such constructor.
Since Numerical
is a concept like this:
template <typename T>
concept Number = ((
std::is_same_v<T, Natural> ||
std::is_same_v<T, Integer> ||
std::is_same_v<T, Rational> ||
std::is_same_v<T, Irrational> ||
std::is_same_v<T, Real> ||
std::is_same_v<T, Complex>
) && requires {
T::symbol; /* Check if 'T' has a static member named 'symbol' */
{ T::symbol } -> std::same_as<const MathSymbol&>; // Check if 'T::symbol' has the type MathSymbol
} );
// Helper to remove const, volatile, and reference qualifiers
template<typename T>
using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
template <typename T>
concept Numerical = Number<remove_cvref_t<T>> || std::is_arithmetic_v<remove_cvref_t<T>>;
So, after some tests, since most of the types are primitives or my strong types, maybe the correct approach should be take them always by value, but here’s the thing. I am not sure.
It’s is possible to have good advice and/or a general rule of thumb of when I should be taking my args by value, by lvalue reference or by rvalue reference (Scott Meyers universal reference) depending on my APIs?
PD: Is just a feeling, but I see the universal references a lot of overkill for my API, but I would like to know in which scenario cases they are really useful.
15