C++部分模板专用化问题

C++ partial template specialization issue

本文关键字:专用 问题 C++      更新时间:2023-10-16

给定一个矩阵类

using index_t = int;
template<index_t M, index_t N, typename S>
struct mat {
// matrix implementation
};

我想有一种通用的方式来获取给定类型T的 elementCount,该方法适用于矩阵和标量。例如,我想象能够做到这一点:

dimensionality<mat<1,2,double>>(); // returns 2
dimensionality<mat<2,2,float>>(); // returns 4
dimensionality<double>(); // returns 1

或者可能是这样的:

attributes<mat<1,2,double>>::dimensionality; // returns 2
attributes<mat<2,2,float>>::dimensionality; // returns 4
attributes<double>::dimensionality; // returns 1

我的尝试:

我尝试执行以下操作(认为我部分专注于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;
};

但是我在指示的行上收到编译器错误。你能帮助我吗(通过建议更好的方法,或者了解我做错了什么(?

首先,专用化不能具有比主模板更多的模板参数,但您的代码为算术情况提供了typename = std::enable_if_t

其次,为了使这种std::enable_if_t发挥作用,它需要产生一些使专业化比主要模板更专业的东西,而不仅仅是有效的。为此,您可以使用void_t技巧:

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;
};

这也意味着矩阵的专用化也应包括以下void参数:

template <index_t M, index_t N, typename S>
struct attributes<mat<M, N, S>, void> {
static constexpr index_t dimensionality = M * N;
};

演示


但是,您的特质可以缩短为:

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;
};

演示 2

也就是说,当没有专用匹配时,只需考虑主模板中的算术类型。

您可以添加另一个默认类型为void的模板参数,然后在算术类型的部分专用化中将std::enable_if指定为相应的模板参数。(并调整mat的部分特化。

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;
};

包装基于特征的静态常量:std::integral_constant

你可能想利用<type_traits>std::integral_constant来实现你的特质,

[...]std::integral_constant包装指定类型的静态常量。它是C++类型特征的基类。

以及提供帮助程序变量模板dimensionality_v以方便使用:

#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;

演示

或者,如果不想为既不满足std::is_arithmetic_v也不等于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;

演示