假设我有一个类似的模板类:
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;
问题是我无法终止模板搜索。
通常我会专门用模板来终止搜索,但我在这里看不到怎么做。
那么你如何专门化这样的模板?
答案 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
规则的递归解决方案同样简单明了。