C++ 递归模板专用化
c++ recursive template specialisation
我编写了一个抽象的容器模板类,如果它对模板参数有意义(即,如果它是数字类型),则应定义数字运算符(一元 + 和 -、二进制 +、- 和 *)。
然后,我想将这些数字运算应用于数值容器的容器(以及数值容器的容器的容器,依此类推)。
我编写了以下代码。(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);
}
主要好处是这个解决方案是开放式的,所以其他人可以重用相同的特征并添加专业化;因为它使用白名单。
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
(以便可以剥离不同的层)。
- 通过递归进行因子分解
- 递归函数计算序列中的平方和(并输出过程)
- 使用递归的数组的最小值.这是怎么回事
- 递归列出所有目录中的C++与Python与Ruby的性能
- 递归计数给定目录的文件和所有目录
- 如何在BST的这个简单递归实现中消除警告
- C++:正在检查LinkedList中的回文-递归方法-错误
- 递归模板化函数不能分配给具有常量限定类型"const tt &"的变量"state"
- 递归无序映射
- TSP递归解的迭代形式
- 如何在Elixir中调用递归函数并行
- 返回递归调用和仅递归调用的区别
- 数组元素打印的递归方法
- 使用递归时获取变量的奇怪值
- 字符串化递归的"std::vector<std::vector<...>>"而不使用部分模板函数专用化
- 是否有可能摆脱模板专用化以停止递归
- C++ 递归模板专用化
- 递归推导缺少的模板专用化的值类型
- 具有递归可变参数模板的函数的部分模板专用化的替代方案
- 内部模板类的递归专用化结束