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

C++ partial template specialization issue

给定一个矩阵class

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

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

您可以添加另一个默认类型为 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;
};

LIVE

首先,特化的模板参数不能多于主模板,但您的代码为算术情况设置了 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;
};

DEMO


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

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

DEMO 2

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

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

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

[...] std::integral_constant wraps a static constant of specified type. It is the base class for the C++ type traits.

并提供辅助变量模板 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;

DEMO.

或者,如果您不想为既不满足 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;

DEMO.