终止模板搜索

时间:2016-06-18 15:04:06

标签: c++ templates constexpr

假设我有一个类似的模板类:

template<typename T, const T* array>
struct NullTArrayLength{
    static constexpr size_t value = NullTArrayLength<T, array+1>::value;
};

/* Some how have a specialization that terminates on when array[0] = 0 */

这样做的目的是找到以0结尾的数组的长度。它将被这样使用:

constexpr char array[] = "this is a test";
constexpr size_t length = NullTArrayLength<char,array>::value;

问题是我无法终止模板搜索。

通常我会专门用模板来终止搜索,但我在这里看不到怎么做。

那么你如何专门化这样的模板?

1 个答案:

答案 0 :(得分:1)

代码中的主要问题不是终止模板递归,而是array + 1不是有效模板非类型参数的事实,如标准中[14.3.2]中所述。对于C ++ 11和14,这样的参数必须具有特定形式& id-expression ,可能省略&。在C ++ 14之后,需求已经放宽并用更新的常量表达式规则表示,但是,参数不能是子对象的地址;这排除了任何指针算术,因为它是根据子对象的地址定义的。

如果你坚持使用基于类模板的解决方案(不是我的第一选择,见下文),你可以通过避免模板参数中的指针算法来解决这个问题。这是一种方法,包括检查以结束模板递归:

#include <iostream>
#include <cstddef>

template<const char* P, std::size_t I = 0, char = P[I]> struct len
{
   static constexpr std::size_t value = len<P, I + 1>::value + 1;
};

template<const char* P, std::size_t I> struct len<P, I, 0>
{
   static constexpr std::size_t value = 0;
};

constexpr char a[] = "this is a test";

int main()
{
   constexpr auto s = len<a>::value;
   std::cout << s << '\n';
}

为清楚起见,我使用char作为元素类型,因为据我所知,这是您正在寻找的内容。该解决方案显然可以扩展到任何T,它可以是非类型模板参数的类型。对于无法直接传递的T,最后一个模板参数可以是bool,它接收调用确定结束条件的constexpr谓词的结果。

然而,一个更简单,更清晰的解决方案涉及在strlen函数中基本实现constexpr;你可以使用这样的调用表达式作为模板参数,即使在C ++ 11中,只要它是一个常量表达式。对于C ++ 14,它很简单:

#include <iostream>
#include <cstddef>

constexpr std::size_t const_len(const char* p)
{
   std::size_t i = 0;
   while(p[i] != 0) ++i;
   return i;
}

extern constexpr char a[] = "this is a test";

int main()
{
   constexpr auto s = const_len(a);
   std::cout << s << '\n';
}

符合C ++ 11 constexpr规则的递归解决方案同样简单明了。