Is it possible in C++
to compare two types without taking in consideration is template arguments? Take this example:
struct Kilogram: public mass<Kilo, kg> {
using dimension = mass;
using ratio = Kilo;
};
struct Hectogram: public mass<Kilo, kg> {
using dimension = mass;
using ratio = Hecto;
};
So, in a concept, I want to check that both types belongs to the same dimension mass
, so I could be able to performn transformations in types related by their dimension, but I could not with different dimensions.
template <typename T>
concept Magnitude = requires {
typename T::dimension;
typename T::ratio;
};
template <typename T, typename R>
concept SameMagnitude = requires {
requires Magnitude<T> && Magnitude<R>;
requires std::is_same_v<typename T::dimension, typename R::dimension>;
};
I explored SFINAE options with std::true_type
and std::false_type
, tag dispathching, but they don't suffice to me, as the tag dispatching one will make me inherit from another type, let's say, mass_dimension
, so my public API starts to be more and more complicated, and I will like to make things as simpler as possible.
How can I compare that both categories have the same dimension of mass, but without taking in consideration the template arguments for the mass type?
EDIT: Where mass:
template<Ratio prefix, Symbol S>
class base_magnitude {}; /* ... more types to represent ratios and symbols */
template <Ratio R, Symbol S>
class mass: public base_magnitude<R, S> {};
So, if I have a new "dimension":
template <Ratio R, Symbol S>
class length: public base_magnitude<R, S> {};
I wanted to performn operations on mass magnitudes, for example, adding grams and kilograms, but detect whenever any type has not the same dimension, let's say, adding meters to kilograms will make fail the concept, because:
struct Kilogram: public mass<Kilo, kg> {
using dimension = mass;
using ratio = Kilo;
};
struct Kilometer: public length<Kilo, km> {
using dimension = length;
using ratio = Kilo;
};
but, as I said before, I still want to yield true in my concept for types that shares the same dimension, as in the previous example, Kilogram
and Hectogram
must yield true, since both are subtypes of mass.
My problem is that the using dimension = mass
is really:
Kilogram -> using dimension = mass<Kilo, kg>...
Hectogram -> using dimension = mass<Hecto, hg>...
so the concept should validate that both have the same dimension mass
, but since my type receives template arguments, they are not the same type.
You could make a generic trait to check if two types are based on the same template:
template<class T, class U>
struct same_template {
static auto test(...) -> std::false_type;
template<template<class...> class C, class... R1s, class... R2s>
static auto test(C<R1s...>, C<R2s...>) -> std::true_type;
static constexpr bool value = decltype(test(std::declval<T>(),
std::declval<U>()))::value;
};
template<class T, class U>
inline constexpr bool same_template_v = same_template<T, U>::value;
template<class T, class U>
concept SameTemplate = same_template_v<T, U>;
Example:
template<class T, class U>
requires SameTemplate<T, U>
void Foo(T&&, U&&) {
}
int main() {
//Foo(Kilometer{}, Kilogram{}); // Error
Foo(Gram{}, Kilogram{}); // Ok (after you've added `Gram`)
}