views:

118

answers:

2

If I change the type to const char str[Len], I get the following error:

error: no matching function for call to ‘static_strlen(const char [5])’

Am I correct that static_strlen expects an array of const char references? My understanding is that arrays are passed as pointers anyway, so what need is there for the elements to be references? Or is that interpretation completely off-the-mark?

#include <iostream>

template <size_t Len>
size_t
static_strlen(const char (&str)[Len])
{
  return Len - 1;
}

int main() {
  std::cout << static_strlen("oyez") << std::endl;
  return 0;
}
+4  A: 

No, the function parameter is a reference to an array of Len const chars. That's how the function knows the length (assuming the last byte is a NUL terminator, hence the -1). The parentheses are there precisely to stop it being what you think it is.

Actually there's no such thing in C++ as an array of references, so it couldn't be what you think it is even without the parens. I guess (but am not sure) that the need for the parens is just for consistency with other similar type definitions, such as pointers to arrays:

void fn(const char *a[3]); // parameter a is of type const char**, the 3 is ignored.
void fn(const char (*a)[3]; // parameter a is a pointer to an array of 3 const chars.

That example also illustrates why an array is not a pointer. Predict the output of the following program, and then run it:

#include <iostream>

void fn(const char (*a)[3]) {
    std::cout << sizeof(a) << "\n" << sizeof(*a) << "\n";
}

void fn2(const char *a[3]) {
    std::cout << sizeof(a) << "\n" << sizeof(*a) << "\n";
}

int main() {
    const char a[3] = {};
    const char **b = 0;
    fn(&a);
    fn2(b);
}

#if 0 
// error: declaration of `a' as array of references
void fn3(const char & a[3]) {
    std::cout << sizeof(a) << "\n" << sizeof(*a) << "\n";
}
#endif
Steve Jessop
Ok, that helps a lot. But why a reference? Isn't the name of an array parameter really just a pointer to the first element?
Ben
@Ben: **No.** Pointers point to an address, arrays are arrays. The name can *decay* to a pointer, but an array is not a pointer. Note my comment on the question: you cannot pass an array "by pointer". You can pass a pointer to the first element, sure, but you aren't passing the array.
GMan
@Steve, in your fn example, is a different value pushed onto the argument stack in those two cases? Does the distinction exist outside the type system?
Ben
@Ben: what two cases? One takes (a pointer to) `a`, and the other takes the value of `b`, which is 0. So yes, a different value is pushed onto the stack. There's nothing directly related to `a` that can be passed into `fn2` - it takes a pointer to a pointer to a char. `a` can decay into a pointer-to-char, but if it does so then the resulting `const char*` is an rvalue, and you can't take a pointer to an rvalue.
Steve Jessop
Ben
@Ben, inside the `fn(` call, a is a pointer to an array of 3 elements. Therefore `sizeof(a)` would be the sizeof a pointer, and `sizeof(*a)` would be `sizeof(const char[3]) == 3`. In `fn2(b);`, `*a` is still a pointer (to const char), and hence you'll (typically) get different results from the two function calls.
KTC
... you could do `const char *c = a; fn2(
Steve Jessop
@Ben, yes you can take the address of a `const` variable. The variable still takes up space in memory, and hence has an address even if you told the compiler not to let you modify it.
KTC
@Ben: you can take the address of any lvalue. An array is a non-assignable lvalue. An integer literal is an rvalue, which is why you can't take the address of it.
Steve Jessop
Ben
Steve Jessop
Ben
@Steve, thanks for seeing through my mistakes :) What I should have said was to do `const char **cp =
Ben
Steve Jessop
A: 

This is one of the way that a function can be made such that the size of the array is passed into the function automatically.

static_strlen(const char (&str)[Len])

is a function that takes in an array of const char of exactly Len elements. The array size must be known at compile time. I.e. the array wasn't allocated via new or malloc.

To be more specific, the parameter is a reference to an array of Len elements, rather than an actual array, which is why it doesn't get converted into a pointer when passed.

KTC