C++ 递归模板专用化

c++ recursive template specialisation

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

我编写了一个抽象的容器模板类,如果它对模板参数有意义(即,如果它是数字类型),则应定义数字运算符(一元 + 和 -、二进制 +、- 和 *)。

然后,我想将这些数字运算应用于数值容器的容器(以及数值容器的容器的容器,依此类推)。

我编写了以下代码。(A)标记显示了我如何尝试解决递归专用化问题。

template <typename T>
struct is_numeric : public std::is_arithmetic<T>{};
template <typename T> /* (A) */
struct is_numeric<GenericContainer<T>> : public std::is_arithmetic<T>{};

/* Classic generic container for non-numeric base types */
template <typename T, bool isNumeric=false>
class BaseContainer : public GenericContainer<T> {};
/* Numeric container: +,-,* operations for numeric base types */
template <typename T>
class BaseContainer<T, true> : public NumericContainer<T> {};
/* Arithmetic base types should map on numeric containers */
template <typename T>
class Container : public BaseContainer<T, is_numeric<T>::value> {};

然后,在测试程序中,我有以下断言:

/* Vector inherits from Container */
typedef Vector<int, 3> V3D;
ASSERT(is_numeric<int>::value);    /* # => OK */
ASSERT(is_numeric<double>::value); /* # => OK */
ASSERT(is_numeric<V3D>::value);    /* # => FAIL */

两个第一断言按预期工作

您的解决方案失败的原因非常具体:模板类型参数专用化将仅匹配确切的类型,而与任何派生类型不匹配。

如果希望派生类型也匹配,则需要切换齿轮并使用另一种策略。在constexpr时代,切换到函数将使您能够使用重载分辨率来发挥自己的优势(作为其中的一种策略):

// Basis
constexpr bool is_numeric_impl(...) { return false; }
template <typename T>
constexpr bool is_numeric(T const& t) { return is_numeric_impl(&t); }
// Specializations
template <typename T,
          typename = std::enable_if<std::is_arithmetic<T>::value>::type>
constexpr bool is_numeric_impl(T const*) { return true; }
template <typename T>
constexpr bool is_numeric_impl(GenericContainer<T> const*) {
    return is_numeric((T const*)nullptr);
}

主要好处是这个解决方案是开放式的,所以其他人可以重用相同的特征并添加专业化;因为它使用白名单

Boost 的

enable_if和类型特征允许像你需要的技巧:

template <class T, class Enable = void> 
struct is_numeric : public std::is_arithmetic<T> {};
template <class T>
struct is_numeric<T, typename enable_if<is_base_of<GenericContainer<T>, T> >::type>
            : public std::is_arithmetic<T> {};

该解决方案采用SFINAE原理,当模板参数满足enable_if内部条件时,编译第二个版本的is_numeric。请注意,is_base_of 的语法是 is_base_of<Base, Derived> 。在 Boost 的enable_if文档中有更多解释。

正如大卫·罗德里格斯(David Rodriguez)善意地提到的那样,由于您案例中的关系更加复杂,因此您可能应该有所不同:

template <template <class> class U, class T>
struct is_numeric<U<T>, typename enable_if<is_base_of<GenericContainer<T>, U<T> > >::type>
            : public std::is_arithmetic<T> {};

如果您不能使用库本身,您可以随时将它们用作灵感:)

你试过吗:

template <typename T>
struct is_numeric : public std::is_arithmetic<T>{};
template <template<class...> class Container, typename T, typename... Rest>
struct is_numeric<Container<T, Rest...>> : public is_numeric<T>{};

似乎对我有用。

您需要为每个容器定义is_numeric特征,不能只使用 base 定义。

template <typename T, size_t N>
struct is_numeric< Vector<T,N> > : is_numeric< GenericContainer<T> > // *
{};

另请注意,is_numeric的定义应与评论中的定义相似,而不是问题中的定义。也就是说,您希望根据嵌套类型是否为数字来定义容器的is_numeric(以便可以剥离不同的层)。