1

Suppose I have a template class like so:

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 */

The goal of this is to find the length of an array terminated with 0. It would be used like so:

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

The problem is that I cannot terminate the template search.

Normally I would specialize the template to terminate the search, but I fail to see how to do so here.

So how do you specialize a template like that?

DarthRubik
  • 3,927
  • 1
  • 18
  • 54
  • What about using a `std::array`? – πάντα ῥεῖ Jun 18 '16 at 15:07
  • @πάνταῥεῖ I cannot do that because I don't want to have to type in the string length (that would defeat the point of having a magic template that gets the length of the string so I don't have to do that) – DarthRubik Jun 18 '16 at 15:08
  • 1
    Is there a higher purpose to this? Can you not just use sizeof(array)? – md5i Jun 18 '16 at 15:11
  • @md5i Earlier I asked [this](http://stackoverflow.com/questions/37892646/take-the-length-of-a-string-implicitly-in-templates) question, which no-one was really able to answer. I am trying this avenue to solve the problem in that question – DarthRubik Jun 18 '16 at 15:14
  • @DarthRubik Well, `constexpr size_t length = sizeof(array);` should give you the wanted result, no need for your template. – πάντα ῥεῖ Jun 18 '16 at 15:17
  • 1
    @DarthRubik `template constexpr size_t arr_siz(T (&)[N]) {return N;}` . Isn't this what is require, or I didn't get the question at all. – Arunmu Jun 18 '16 at 15:55
  • @bogdan The solution is to use [void_t](http://stackoverflow.com/questions/27687389/how-does-void-t-work) – DarthRubik Jul 01 '16 at 23:04
  • @bogdan Your solution is actually shorter, but mine can be used to terminate any template, arbitrarily.....but yours is actually way simpler – DarthRubik Jul 02 '16 at 13:12

1 Answers1

1

The main problem in your code is not terminating the template recursion, but rather the fact that array + 1 is not a valid template non-type argument, as specified in [14.3.2] in the Standard. For C++11 and 14, such an argument has to have a specific form, & id-expression, possibly omitting the &. After C++14, the requirements have been relaxed and expressed in terms of the updated rules for constant expressions, but still, an argument cannot be the address of a subobject; this excludes any pointer arithmetic, since that's defined in terms of addresses of subobjects.

If you insist on using a solution based on class templates (not my first choice, see below), you can work around that by avoiding pointer arithmetic in the template argument; here's one way to do it, including the check to end the template recursion:

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

For clarity, I used char as the element type, since that's what you're looking for as far as I understand. The solution can obviously be extended to any T that can be the type of a non-type template parameter. For a T that can't be passed directly, that last template parameter can be a bool that receives the result of calling a constexpr predicate that determines the end condition.

However, a much simpler and clearer solution involves basically implementing strlen in a constexpr function; you can use such a call expression as a template argument, even in C++11, as long as it's a constant expression. For C++14, it's as simple as this:

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

A recursive solution complying with the C++11 constexpr rules is equally straightforward.

bogdan
  • 9,229
  • 2
  • 33
  • 48