I am new to C++ templates and encountered these C++ templates related codes but is not able to understand their meaning:
class StringBuffer
{
CharBuffer cb;
..
template <size_t ArrayLength>
bool append(const char (&array)[ArrayLength]) {
return cb.append(array, array + ArrayLength - 1); /* No trailing '\0'. */
}
};
What does the bool append(const char (&array)[ArrayLength]) mean? It seems to me that the function template will be instantiated to something taking a parameter with a specific ArrayLength. But isn't that we cannot specify an array length in the parameter list of a function? Also what does const char (&array) mean? Shouldn't it be something like const char &(without the parentheses)?
I am reading the book C++ Templates The Complete Guide by David Vandevoorde/Nicolai M.Josuttis, which part of the book covers the above syntax?
It means "reference to const char array".
The reason for it is that if you pass like
template <int S>
void f(T a[s]){}
You will lose the size information according to "array parameter deprecation rules", mainly because pointer doesn't hold array size information. (AKA standard said so.)
So you will have to pass by reference and not by pointer value.
The parenthesis before [] is required because [] will take precedence in front of &, so in order to make & take precedence it needs to be done like
T (&a)[s]
const char (&array)[ArrayLength]
is a reference to an array of ArrayLength objects of type char.
Without the parentheses, it would be an array of references, which is not allowed. Without the &, it would be an array which (as a function parameter) decays to a pointer, losing the information about the size of the array.
It seems to me that the function template will be instantiated to something taking a parameter with a specific ArrayLength.
That's right. The array length is known at compile time, and this will instantiate a function that can use that compile-time value.
But isn't that we cannot specify an array length in the parameter list of a function?
Yes, you could supply the length as an extra function parameter; but that would be a runtime value, and there'd be know way to validate that it was correct. The template ensures that the template argument really is the size of the array.
which part of the book covers the above syntax?
I don't have that book but, looking at the table of contents I'd suggest looking at 4.2 (Nontype Function Template Parameters) and 11 (Template Argument Deduction) for this kind of thing.
It's the syntax for passing the array by reference (since arrays can't be passed by value in C++):
void foo(const char (&array)[10]) { ... } // We can pass an array of lenth 10
Now throw a template parameter in the mix instead of the 10. The compiler knows the size of an array at compile time and can instantiate the template with correct value.
template<size_t N>
void foo(const char (&array)[N])
{
// use N, it'll be whatever the size of the array you instantiate the template with is
}
This syntax will set a template parameter based on the size of a statically allocated array argument.
The templated version of "append" (that you included) calls an overload which takes 2 arguments: a pointer to char and a count (you did not include this).
So you might have an array like:
const char my_string[] = "hi";
You would use the "append" member function like this:
my_string_buffer_object.append(my_string);
And the length of my_string will be auto-detected, setting the ArrayLength parameter to the length of my_string. Then a more verbose version of "append" is called with the string length automatically filled in for you.
Basically, this version of "accept" wraps another version. It lets you pass an array as the only argument, automatically filling in a length using the template parameter's info.
If you use this syntax, keep in mind that these array length parameters count elements and not object sizes (what sizeof would tell you about the array). For char these are the same, but arrays with larger-sized element types will produce a template array length parameter smaller than its sizeof.
The given code is a good lesson:
at first , It want to pass a array , So can't pass by value, then pass by refrence (&) , Then it pass it by const word that safe pass it.
You know C/C++ has limitation in array, So programmer of this code defined a template for length of Array, and solve this problem.
Related
The following code doesn't compile:
template <int N>
void f(char[N]) {}
int main() {
char buf[10];
f(buf);
}
If I change the char[N] to char (&)[N], it works. So what the difference between them?
You have been bitten by backwards compatibility with C. When you declare a function like:
int f(char c[10]);
You declare a function whose argument is of type char *. The compiler decays the argument type for you. The problem is:
int f(char c[5]);
declares the same function. This is the way C worked, and C++ retained it for compatability.
int f(char (&c)[10]);
Declares a function whose argument is of type "reference to array (length 10) of char". C didn't have references, so there is no need to maintain backwards compatibility.
int f(char (&c)[5]);
Declares a different function - with a different argument type.
obviously you are aware that char [N] is an array and char (&)[N] is a reference to a char [N].
c-style arrays are special when passed as arguments by value. The array itself is not passed, but a reference is.
This 'magic' is an historic side-effect of c++'s evolution from C.
to pass an array by value these days we'd use std::array<char, N> which encapsulates the c-style array.
note that char (&)[N] is treated as a literal type and so may be passed to constexpr functions in constexpr context.
I think the order of events is
The compiler does not look for a function taking an char[10] proper because the language doesn't support passing arrays as values.
The compiler looks for a function taking a reference to char[10] (and doesn't find any).
The compiler looks for a function taking a pointer to char, which is the actual argument type after what's called type adjustment, and doesn't find any.
The last point is interesting because the template function f actually does take a pointer to char, as other posters explained: the index in the declaration is superfluous, and in a function declaration f(char p[]) p is not of type array but of type pointer to char. Note that that differs from declarations of that kind elsewhere (not as a function parameter), where p would be an array, albeit incomplete.
The reason the compiler cannot instantiate the function template is not that its argument type is wrong: It would match after argument type adjustment. The reason is simply that it cannot deduce the template parameter N from the argument. After all, for each N there would be a different f: which one should the compiler take?? After the actual argument buf has been "adjusted" to a pointer to its first element, the length information in the argument is lost. (Yes, compilers are stupid.)
The length information was retained when you declared the function as taking a reference to an array.
Another possibility is to simply instantiate the template function explicitly:
f<10>(buf); works.
While answering my question in codereview.stackexchange, it was suggested to use,
template <class T, size_t size>
T peak(const T (&arr)[size]) {
Instead of,
int peak(int * arr, size_t size)
{
Now I have two questions here,
How compiler can calculate size, while C++ compiler cannot bound check
What if I use arr[size] instead of (&arr)[size]. I mean is there anything like (arr&)[size]. If yes, what is the difference.
In the first case, you pass the array by reference. The function will only accept arrays (and not pointers). Arrays are always of known size, which is known by the compiler and unpacked in the template parameters.
In the second case, you can pass any pointer or array (the latter will decay to a pointer), but you have to specify the size since the compiler cannot infer the size of the memory a pointer points to.
How to find the size of string array passed to a function. The size should be computed inside the function.
#include<iostream>
using namespace std;
template <typename T,unsigned S>
unsigned arraysize(const T (&v)[S]) { return S; }
void func(string args[])
{
unsigned m=arraysize(args);
cout<<m;
}
int main()
{
string str_arr[]={"hello","foo","bar"};
func(str_arr);
}
What i dont understand is:
If the statement arraysize(str_arr) is used in main,it wouldn't pose a problem. The str_arr is an array, so str_arr acts as a pointer, so when we use arraysize(str_arr) that means we're sending the address to arraysize function.(correct me if i'm wrong).
But in function func(), i dont understand why there is a problem, i.e. the statement arraysize(args) sends the address of the string array args(or the address of pointer args).. or is it more complicated since it becomes some double pointer?? Explain?
Also please correct the above code..
str_arr is an array of strings. When you do sizeof(str_arr), you get the size of that array. However, despite the fact that args looks like an array of strings, it's not. An argument that looks like an array is really of pointer type. That is, string args[] is transformed to string* args by the compiler. When you do sizeof(args) you are simply getting the size of the pointer.
You can either pass the size of the array into the function or take a reference to the array with a template parameter size (as you did with arraysize):
template <size_t N>
void func(string (&args)[N])
{
// ...
}
There is no way to determine the size of an array when sent to a function. You also have to remember that only a pointer to the array is sent to the function, which makes it even theoretically quite implausible to calculate the array's size.
The information of the array's size is never visible in your function, as you threw it away when you decided to use string args[] for the argument. From the compiler's perspective, it's the same as string* args. You could change the function to:
template<size_t M>
void func(string (&args)[M])
{
cout<<M;
}
but it seems you already know that, right?
If the statement arraysize(str_arr) is used in main,it wouldn't pose a
problem. The str_arr is an array, so str_arr acts as a pointer, so
when we use arraysize(str_arr) that means we're sending the address to
arraysize function.(correct me if i'm wrong).
I have to correct you here. You state a correct premise, but draw the wrong conclusion.
The key point is indeed that str_arr is an array in main. While an array decays to a pointer in many (most) expression contexts, this does not apply when a reference to array is initialized. That is the reason why array_size is declared to take a reference to array parameter - this is the only way to have a parameter of array type, which implies that it comes with a defined length.
That is not the case for func. When a function parameter is declared to be of plain array type, the the array to pointer decay is applied to that declaraction. Your declaration of func is equivalent to void func(string * args). Thus args is a pointer, not an array. You could call func as
string str_non_array;
func(&str_non_array);
Because of this, a reference-to-array can't bind to it. And anyways, args has completely lost all information about the size of the array it is pointing to.
You could use the same reference-to-array trick as is used in arraysize: declare func as
template <std::size_t N>
void func(string (&args)[N]);
But this gets impractical to do everywhere (and may lead to code bloat, if applied naively to all array-handling code). The C++ equivalent of an array-with-length as available in other languages is std::vector<string> (for dynamically sized arrays) or std::array<string,N> (for fixed size known at compile time). Note that the latter can cause the same code bloat as mentioned above, so in most cases, std::vector<string> would be the preferred type for array that you need to pass to various functions.
Dmitry is right and I would like to explain it a bit further. The reason its happening is because array is not a First Class citizen in C++ and when passed as parameter it decays to pointer and what you get in called function is a pointer to its first element and size is lost.
You can refer C++ arrays as function arguments to see what alternative options are available.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to find the sizeof(a pointer pointing to an array)
In the first call to strcpy_s the compiler can deduce the array length, but in the second call the array length has to be passed in.
TCHAR szTemp[512];
::strcpy_s(szTemp, "a long text message");
TCHAR* pszTemp = new TCHAR[512];
::strcpy_s(pszTemp, 512, "a long text message");
delete []pszTemp;
How does the compiler do this? Is this a Microsoft only extension? Also, how could I declare my methods to take advantage of the parameter deduction?
In the first use of strcpy_s, the number of elements in the array szTemp is part of the type of szTemp (since it is an “array of 512 TCHAR”), so the compiler knows about it and can complete the template that declares strcpy_s.
In the second use of strcpy_s, pszTemp is a pointer (it is a “pointer to TCHAR”), not an array, and the number of elements pointed to is not part of the type. In general, a compiler cannot know how many elements are at the place where a pointer points. (In this case, a compiler might deduce it, since the preceding code shows it, but that adds complications to the compiler and the language that are generally regarded to be not worth implementing.)
To do this yourself, declare a template the way strcpy_s is declared:
template <size_t size> errno_t strcpy_s(
char (&strDestination)[size],
const char *strSource
);
This declares a template based on parameter size, and the template is for a function whose first parameter has the type “reference to an array of size elements of char”. When the compiler sees the use of strcpy_s with a first argument that is an array of 512 elements of char, it is able to match this argument to the parameter in the template, and it deduces that size is 512.
Somewhere else, you will have a definition of the template (not just a declaration). That definition can use the template parameter size in its code. When the compiler sees the use of strcpy_s, it will instantiate the template definition in a specialization where size is 512.
This only works in C++, not C, because C does not have templates.
According to docs, strcpy_s exists as a concrete function, but also as a template function overloaded for arrays of known size, such as the szTemp array you provide. In that case the size of the array is the template parameter of the function definition.
strcpy_s is itself a Microsoft-only function, so the template version is no more Microsoft-specific than the version that accepts a pointer.
The first is using a template, something like:
template <size_t N>
strcpy_s(char (&dest)[N], char const *source) {
// ...
}
Inside the template, N is the size of the array that was passed.
In your second case, pszTemp is a pointer instead of an array, so it won't match up with the template parameter (which requires an actual array), so you need to pass the destination size explicitly.
In the first case the template function is used (doc), so there's no magic:
template <size_t size>
errno_t strcpy_s(
char (&strDestination)[size],
const char *strSource
); // C++ only
It can deduce the array length for szTemp because it is declared as an array. For pszTemp, it is declared as a pointer and there is no way to deduce its length.
There are supposedly some third party strcpy_s implementations, but mostly it is a Microsoft thing--it was part of their effort to harden up their security holes.
Is it type[]? For example, could I have
T<int[]>;
for some template T.
The type of an "array of type T" is T [dimension], which is what you could pass as template parameters. E.g.:
someTemplate<int [10]> t; // array type as template parameter
int a[5]; // array of 5 ints named 'a'
Arrays need to have a dimension which must be greater than 0. This means that e.g. U u[]; is illegal.
There are cases that might seem like exceptions, the first being parameters:
void f(T[]);
This is a special rule for parameters and f() is actually equivalent to the following:
void f(T*);
Then there is direct inialization of arrays:
int a[] = { 1, 2, 3, 4 };
Here the array size is implicitly given through the number of elements in the initializer, thus the type of a is int[4].
There are also incomplete array types without specificied bounds, but you can't directly create instances of these (see Johannes answer for more):
template<class T> struct X { typedef T type; };
X<int[]>::type a = { 1, 2, 3 };
If you are looking for dynamic arrays, prefer standard containers like std::vector<T> instead.
There are two syntaxes to denote array types. The first is the type-id syntax and is used everywhere where the language expects a compile time type, which looks like:
T[constant-expression]
T[]
This specifies an array type that, in the first form, has a number of elements given by an integer constant expression (means it has to be known at compile time). In the second form, it specifies an array type with an unknown number of elements. Similar to class types that you declare without a body, such an array type is said to be incomplete, and you cannot create arrays of that type
// not valid: what size would it have?
int a[];
You can, however, specify that type. For example you may typedef it
typedef int unknown_int_array[];
In the same manner, you may specify it as a template type argument, so the answer to your question is yes you can pass such a type specifier to a template. Notice that i talk about specifiers here, because the form you use here is not the type itself.
The second way is using the new-type-id syntax which allows denoting runtime types by having non-constant bounds
T[expression]
This allows passing variables as element count, and also allows passing a zero. In such a case, a zero element array is created. That syntax is only usable with the new operator for supporting dynamic arrays.
If possible, you might consider instead using dynamic arrays, and passing in a pointer as the templated type. Such as...
T<int*> myVar;
This started as a comment to Georg's answer, but it ran a bit long...
It seems that you may be missing some key abstraction in your mental model of arrays (at least C-style ones). Local arrays are allocated on the stack with a hard-coded size. If you have an array inside a class or struct, the space for the array is part of the object itself (whether on the stack or heap). Global arrays may even be represented directly in the size of the executable.
This means that any time you want to use an array, you must specify its size to the compiler. The only reason you can leave the brackets empty in a parameter list is because functions treat array parameters as pointers. The function would hardly be useful if it could only operate on one size of array.
Templates are no exception. If you want the size of the templated array to vary, you can add an extra template parameter. You still have to specify the size at compile time for any given instance, though.
The syntax for declaring arrays is
<type> <variable>[<size>];
When using a template the declaration is, in example
template <class T>
T var[4];