Reputation: 6959
Given a matrix class
using index_t = int;
template<index_t M, index_t N, typename S>
struct mat {
// matrix implementation
};
I would like to have a generic way of obtaining the elementCount for a given type T
that will work for both matrices and scalars. For example, I imagine being able to do this:
dimensionality<mat<1,2,double>>(); // returns 2
dimensionality<mat<2,2,float>>(); // returns 4
dimensionality<double>(); // returns 1
or perhaps something like this:
attributes<mat<1,2,double>>::dimensionality; // returns 2
attributes<mat<2,2,float>>::dimensionality; // returns 4
attributes<double>::dimensionality; // returns 1
I tried doing the following (thinking that I am partially specializing the struct attributes
):
template<typename T>
struct attributes {};
template<typename S, typename = std::enable_if_t<std::is_arithmetic<S>::value>>
struct attributes<S> { // <--- compiler error on this line
static constexpr index_t dimensionality = 1;
};
template<index_t M, index_t N, typename S>
struct attributes<mat<M, N, S>> {
static constexpr index_t dimensionality = M * N;
};
but I get a compiler error on the indicated line. Can you help me (either by suggesting a better approach, or to understand what I'm doing wrong)?
Upvotes: 2
Views: 70
Reputation: 73206
You may want to make use of std::integral_constant
from <type_traits>
to implement your trait,
[...]
std::integral_constant
wraps a static constant of specified type. It is the base class for the C++ type traits.
as well as supply a helper variable template dimensionality_v
for ease of use:
#include <type_traits>
// Default dimensionality 0.
template <class T, typename = void>
struct dimensionality : std::integral_constant<index_t, 0> {};
template <typename S>
struct dimensionality<S, std::enable_if_t<std::is_arithmetic_v<S>>>
: std::integral_constant<index_t, 1> {};
template <index_t M, index_t N, typename S>
struct dimensionality<mat<M, N, S>> : std::integral_constant<index_t, M * N> {};
template <class T>
inline constexpr index_t dimensionality_v = dimensionality<T>::value;
DEMO.
Alternatively, if you don't want to allow a default dimensionality for types that are neither fulfilling std::is_arithmetic_v
nor equals mat
:
template <class T, typename = void>
struct dimensionality {};
template <typename S>
struct dimensionality<S, std::enable_if_t<std::is_arithmetic_v<S>>>
: std::integral_constant<index_t, 1> {};
template <index_t M, index_t N, typename S>
struct dimensionality<mat<M, N, S>> : std::integral_constant<index_t, M * N> {};
template <class T>
inline constexpr index_t dimensionality_v = dimensionality<T>::value;
DEMO.
Upvotes: 1
Reputation: 173024
You could add another template parameter with default type void
, then specify the std::enable_if
as the corresponding template argument in the partial specialization for arithmetic types. (And adjust the partial specialization for mat
too.)
template<typename T, typename = void>
struct attributes {};
template<typename S>
struct attributes<S, std::enable_if_t<std::is_arithmetic<S>::value>> {
static constexpr index_t dimensionality = 1;
};
template<index_t M, index_t N, typename S>
struct attributes<mat<M, N, S>, void> {
static constexpr index_t dimensionality = M * N;
};
Upvotes: 1
Reputation: 48527
First, the specialization cannot have more template parameters than the primary template, but your code puts typename = std::enable_if_t
for the arithmetic case.
Secondly, in order to make this std::enable_if_t
to work, it would need to result in something that makes the specialization more specialized than the primary template, not only valid. For that, you could use the void_t
trick:
template <typename T, typename = void>
struct attributes {};
template <typename S>
struct attributes<S, std::enable_if_t<std::is_arithmetic<S>::value>> {
static constexpr index_t dimensionality = 1;
};
This also entails that the specialization for matrices should include this void
parameter as well:
template <index_t M, index_t N, typename S>
struct attributes<mat<M, N, S>, void> {
static constexpr index_t dimensionality = M * N;
};
Your trait could, however, be shortened to:
template <typename T>
struct attributes {
static_assert(std::is_arithmetic<T>::value, "T must be arithmetic or mat");
static constexpr index_t dimensionality = 1;
};
template <index_t M, index_t N, typename S>
struct attributes<mat<M, N, S>> {
static constexpr index_t dimensionality = M * N;
};
That is, just consider arithmetic types in the primary template, when no specialization matches.
Upvotes: 1