I have a generalized modulo struct called quotient_ring
. The relevant bits are shown below.
template <typename R = long long>
struct quotient_ring{
using Q = quotient_ring;
R x, m;
...
template <typename T>
friend constexpr std::basic_ostream<T> &operator<< (std::basic_ostream<T> &str, const Q &q){
return str << '(' << q.x << ")%(" << q.m << ')';
}
};
This operator <<
would print something like 2
mod 7
as (2)%(7)
. The reason I need the brackets is because the type R
can become very nested. However, if R
is only an arithmetic type, such as long long
, I would like to print without the brackets. I found that one way to accomplish this is as follows.
template <typename T>
friend constexpr std::basic_ostream<T> &operator<< (std::basic_ostream<T> &str, const Q &q){
if constexpr (std::is_arithmetic<R>::value) return str << q.x << '%' << q.m;
else return str << '(' << q.x << ")%(" << q.m << ')';
}
I think this is a fine solution. However, I would like to know if the same can be achieved by means of template specialization. I tend to personally like template specialization more than branching on type traits.
I tend to personally like template specialization more than branching on type traits.
Why? if constexpr
is a compile-time branch. The equivalent SFINAE is far less readable.
template <typename R = long long>
struct quotient_ring{
using Q = quotient_ring;
R x, m;
template <typename Char>
friend constexpr std::enable_if_t<
std::is_arithmetic_v<R>,
std::basic_ostream<Char> &
>
operator<< (std::basic_ostream<Char> &str, const Q &q){
return str << q.x << '%' << q.m;;
}
template <typename Char>
friend constexpr std::enable_if_t<
!std::is_arithmetic_v<R>,
std::basic_ostream<Char> &
>
operator<< (std::basic_ostream<Char> &str, const Q &q){
return str << '(' << q.x << ")%(" << q.m << ')';
}
};
int main() {
quotient_ring<quotient_ring<>> ring{
{1, 2},
{3, 4}
};
std::cout << ring << '\n'; // (1%2)%(3%4)
}
Might I suggest putting some spaces in the output (like (1 % 2) % (3 % 4)
) to make it more readable?