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.
Related
By "normal variable declarations", I assume the declarations like the below.
int a = 3;
If you define a parameter of a function, you will write like this:
void func(int a) {
<statement>
}
Let's assume a more complicated case. When you normally declare an array of pointer to functions, you will write
int (*p[3])(int, double);
and if you'd like to set this array as a function parameter, you will write
void func(int (*p[])(int, double)) {
<statement>
}
Now I found that, in any cases, the both ways look almost the same. However, I don't find what kind of "written evidence" assure this rule. In other words, when I write a super complex normal declaration, by what can I believe that I'm able to set the object as a function parameter with the way I just now used in normally declaring the object? As far as I know, this rule is true, but it seems there is no evidence, though I visited so many websites in English and Japanese and even read the C++11 draft.
Does anyone have the evidence?
C language answer:
Regular variable declarations and parameter declarations are not always identical. This is regulated by the C standard C11 6.7.6.3 "Function declarators", where we can read
The only storage-class specifier that shall occur in a parameter declaration is register.
This alone is a major difference. We cannot have parameters that are static or extern - they can only be auto(default) or register.
A declaration of a parameter as ‘‘array of type’’ shall be adjusted to ‘‘qualified pointer to
type’’,
Meaning all parameters that are declared as arrays get adjusted to a pointer to the first element of that array. This is what happens in your example!
int (*p[3])(int, double); or
int (*p[])(int, double); or
int (*p[666])(int, double);
when written as a parameter to a function, all get adjusted ("decay") into a pointer to the first element. That is, a pointer to a function pointer to a function accepting int, double and returning int. Aka this monstrosity: int (**p)(int, double);
You get away with the empty [] just because the compiler doesn't care about the array boundaries. This would actually create an array of incomplete type (which wouldn't be allowed), but the compiler adjusts it to a pointer to the first element instead, so the array size is irrelevant, as that part is lost during "adjustment" anyway.
A declaration of a parameter as ‘‘function returning type’’ shall be adjusted to ‘‘pointer to
function returning type’’, as in 6.3.2.1.
Meaning that if you get the crazy idea to pass a whole function to another function, it gets adjusted to a function pointer. Meaning that crazy code like void func( int p (int, double) ) is actually valid and equivalent to void func( int (*p)(int, double) ). This is a special case for function parameters.
Disclaimer This answer was written when C++ was the sole language tag of the question.
When you normally declare an array of pointer to functions, you will
write
int (*p[3])(int, double);
No. That is a false premise.
You write it like this:
using My_callback_t = int (int, double);
std::array<My_callback_t*, 3> p;
// or
std::array<std::function<My_callback_t>, 3> p;
Or use std::vector instead of std::array for more flexibility.
The rest of the question becomes moot in my opinion, but for your amusement this is the monstrosity you are asking for:
void func(int (*(&p)[3])(int, double))
{
}
Just in case I wasn't clear enough: Don't write this!!
void func(int (*(&p)[3])(int, double))
^ p
^ is a reference
^ to an array
^ of 3 elements of type
^ pointer
^ to function
^~~~~~~~~~~~ receiving 2 params of type int and double
^~~ and returning int
^~~~~~~~~~~~~~~~~~~~~~~~~~ Kill. Me. Now!
My function f(void *data[]) is supposed to receive an array of generic pointers as an argument, as far as I understand.
Nonetheless, I get a compilation error when I try to do
size_t *cnt[8];
... // initialize pointers in cnt
f(cnt);
In particular g++ automatically converts the prototype of f into f(void**), then it converts cnt to size_t**, and than it says that it cannot convert size_t** to void**.
g++ automatically converts the prototype of f into f(void**)
I find that using exact terminology helps understand programming languages better. It may be confusing to think that f is converted into f(void**). That's not pedantically speaking the case.f(void *data[]) is simply another way to write f(void** data). This, alternative syntax, is a case of syntactic sugar.
Conversion typically means change of one type into another. There is no conversion involved in the declaration of your function.
Also, this syntactic sugar is not specific to the compiler g++. This behaviour is specified in the C++ standard.
f(cnt);
then it converts cnt to size_t**
This is a conversion indeed. It is a special type of conversion called decaying. This implicit conversion is also a case syntactic sugar. It is semantically same as writing:
f(&cnt[0]);
Cannot pass size_t *arg[] to function requiring array of void pointers
This is correct.
Indeed, your function expects a void** but you passed the function a size_t**. A size_t** is not implicitly convertible to void**, so your program is ill-formed.
Ways to fix the problem:
1) Use f(size_t**) instead.
2) Use an array of void* in the first place
3) Copy size_t *cnt[8] into another array void *temp[8], and pass that to the function.
4) (not recommended) Cast cnt to void** and hope that it does what your function expects.
Whereas C allows implicit conversion from any type pointer-to-pointer to void pointer-to-pointers, C++ does not allow this. You would need to explicitly cast cnt to void **.
I have this pair of declaration/definition on my Eclipse IDE (in a .h and .hpp respectively):
A( T* [] );
and
A<T>::A(T ** p_proc) { ... }
The first is an array of pointers, the other a pointer of pointers.
I am confused in that they are interchangeable here; Eclipse complains if I replace the ** by * [], throwing a syntax error. Eclipse does not raise errors when I do the opposite, though.
My question is twofold; are these two notations fundamentally the same? Are T [][] and T ** the same as well?
Why does Eclipse throw a syntax error when the hpp file has a type of * [], but not in the header?
My question is twofold; are these two notations fundamentally the same?
No, they are not. T*[] has type array of unknown size of pointer to T whereas T** has type pointer to pointer to T. Arrays and pointers are not identical in general.
However, declaring a function parameter to be of array type is exactly the same as declaring it to be of the corresponding pointer type. If a function parameter is specified to have array type then it's "adjusted" to have the corresponding pointer type. In the same way, int* and int[] aren't the same type, but when you write a function that takes an int[] parameter, it's adjusted so that it takes an int* parameter. (Note that this adjustment is suppressed if the parameter is a reference to array.)
Are T [][] and T ** the same as well?
Actually T[][] is not a valid type at all. In a multidimensional array type, only the first bound may be omitted.
Why does Eclipse throw a syntax error when the hpp file has a type of * [], but not in the header?
Probably because you're writing T*[] p_proc. The correct declarator is T* p_proc[].
They're the same for parameters to functions. A function parameter can't have the type "array of T". If you try to declare a function parameter as having a type "array of T", the compiler will (silently) adjust it to "pointer to T".
That's not the case elsewhere though. Just for example, having something like:
//filea.cpp
int x[SIZE];
and:
//fileb.cpp
extern int *x;
...will not work.
If your Eclipse is not understanding the following case, it's a bug:
template<typename T>
struct A{
A(T **p_proc);
};
template<typename T>
A<T>::A(T *p_proc[]) {}
It's perfectly fine.
Check question about arrays decaying to pointers to understand it in more detail.
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.
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.