简体   繁体   中英

Const char array with template argument size vs. char pointer

I saw a construct of the following type in some code today:

template<unsigned int N> unsigned int f(const char (&a)[N]);

Is there any reasonable point in having this over having:

unsigned int f(const char *a);

I vaguely understand the pointer sharing implications of the latter, but is it really so bad, that it needs to be replaced by an obscure code twice the size?

(Unfortunately, I cannot ask the author of the code, otherwise I would)

The intent of passing any raw pointer to a function is the caller has some idea of:

  • What it points to.
  • How many it points to.

C-style strings as input parameters have the latter inferred, as the assumption is that "how many" is deemed by arrival at the null char terminator.

But what if you're not passing a C-style string? What if it is simply a sequence of zero-or-more char values? Well, if that is the case, then:

void f(const char *s)
{
    // how many char does s refer to?
}

The logical deduction would be to do this:

void f(const char *s, std::size_t N)
{
    // Now the caller is telling us there are N chars at s
}

and this is not uncommon at all, albeit a potential point of error if the caller passes us the wrong length (never say never).

But what if there were a way to slurp that data from the actual variable type being passed to the function using deduction via non-type template parameter? What if the caller is invoking us with a fixed array?

template<std::size_t N>
void f(const char(&ar)[N])
{
    // we know the caller is passing a const-reference to a
    // char array declared of size N. The value N can be used
    // in this function.
}

Now we know both items in our list: the "what" and the "how many". Furthermore, we can now provide both a template function and an overload and have both worlds available to us:

// length specified implementation
void f(const char *s, std::size_t N)
{
    // caller passed N
}

// fixed buffer template wrapper
template<std::size_t N>
void f(const char(&ar)[N])
{
    f(ar,N); // invokes length-specified implementation from above.
}

And both of the following will work:

int main()
{
    char buff[3];

    f(buff,3);
    f(buff);

}

So how is this good? Because the following will flag a compiler error, as no matching implementation can be found:

int main()
{
    char buff[3];
    const char *ptr = buff;
    f(ptr); // ERROR: no matching function f(const char *)
}

In summary it is a common technique to assist in providing both items in our bullet list to the callee: the "what" and the "how much", without having to long-hand sizeof(ar)/sizeof(*ar) every time you use a fixed-length native array as your input parameter.

Best of luck.

Pointers do not keep information whether they point to a single object or the first object of an array. So if you for example write within the body of function

unsigned int f(const char *a);

expression

sizeof( a ) / sizeof( *a )

you will get only the size of the pointer itself. While if you would use the same expression within the body of function

template<unsigned int N> unsigned int f(const char (&a)[N]);

you will get the size of the array (of course you could use simply value of N).

So when the second approach is used then usually such functions are declared with two parameters where the second parameter specifies the size of the array

unsigned int f(const char *a, size_t n);

Consider a situation when you need to append the character array passed as an argument with other string (let's assume that qualifier const is absent). When you use the second declaration then even function strlen applied to the pointer will not help you to determine whether the original string is large enough that could be appended. In the first approach you can use expression N - strlen( a ) that to determine whether there is enough space in the array.

template<unsigned int N> unsigned int f(const char (&a)[N]);

This function template takes in the array by reference, which means when used directly with statically allocated C-Arrays the function knows the array size at compile.

unsigned int f(const char *a);

Here all the function knows is that it has been given some pointer to a const char. So if you wanted to use it to manipulate an array it would have to take in the array size as an additional argument since there is no way to retrieve this information from the pointer alone.

The template version takes an array of a certain size. The pointer version takes only a pointer, without any concept of the size of the data pointed to by the pointer.

The template in instantiated for each distinct value of N (so there's extra machine code generated) but inside f it's free to use its knowledge of N to do whatever's useful (eg process the right number of entries from a , correspondingly size another character array it might need). You have to look inside f to know whether N is actually used, and if it's useful. With the non-template version the caller's not providing an array/data size, so there's no equivalent information provided (if the data a points to is variable in length then f would have to rely on some other way to know how much data there is, eg strlen() taking the time to search for a terminating NUL).

There are two reasons to use that :

  • it is not possible to pass to that template function anything but c-style arrays. So, calling that method with a pointer would be a compilation error.
  • in the template function you get the size of the array, evaluated during the compilation

So, this :

const char* p="abc";
f(p);

causes the compilation to fail :

garbage.cpp:39:5: error: no matching function for call to ‘f(const char*&)’
  f(p);
     ^
garbage.cpp:39:5: note: candidate is:
garbage.cpp:21:39: note: template<unsigned int N> unsigned int f(const char (&)[N])
 template<unsigned int N> unsigned int f(const char (&a)[N])

Whereas, this :

f("abc");

is fine, and you get the size of this array determined as a compilation constant

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM