"Leor Zolman" <le**@bdsoft.com> wrote "Canonical Latin" <ja******@hotmail.com> wrote:
... But I'm still curious as to the rational of having type pointer-to-array-of-size-N-of-type-T (which is fine) and not having type array-of-size-N-of-type-T (with some exceptions, which is curious). So far the consensus seems to be that while everyone is aware of this no one
knowsthe rational behind it. I'm sure there must be a compelling reason for
thisand once explained it would be obvious :)
I know you asked me not to answer any more of your questions, but I'm stubborn ;-)
The place where you can't have array-of-size-N is in the special case of a function parameter. Otherwise they're fine.
In the case of function parameters, if an array got passed by value, that would be a very expensive operation. Most of the time folks would have to program around it, by writing an expression that evaluates to a pointer of some kind and passing /that/. But believe it or not, if arrays got passed by value, that would actually be the special case...because of the following:
As I'm fond of saying, arrays are just "smoke and mirrors" anyway; they're mostly syntactic sugar, and compilers translate array names into pointers to their first elements (there are a few exceptions, such as when applying sizeof). That's true even if you don't pass them to a function.
So, given: int a[10]; a[3] = 5;
that last line actually compiles into something like: *(&a[0] + 3) = 5;
Therefore, in a function call such as: func(a);
it makes perfect sense what gets passed is a pointer to the first element of the array:
func(&a[0]);
and thus there's no information available to the function about the size
of that array. -leor
-- Leor Zolman --- BD Software --- www.bdsoft.com On-Site Training in C/C++, Java, Perl and Unix C++ users: download BD Software's free STL Error Message Decryptor at: www.bdsoft.com/tools/stlfilt.html
Type checking and passing by value are two different issues. Consider:
fun1 (T[]); // pointer-to-type-T
fun2 (T[2]); // array-of-size-2-of-type-T
fun3 (T[3]); // array-of-size-3-of-type-T
fun4 (T**); // pointer-to-pointer-to-type-T
fun5 (T*[2]); // pointer-to-array-of-size-2-of-type-T
fun6 (T*[3]); // pointer-to-array-of-size-3-of-type-T
In none of the cases the whole array is passed by value and this is not the
issue. Suppose that c++ actually did have a legitimate type
array-of-size-N-of-type-T. Then
fun (T x[]); // fun1
fun (T x[2]); // fun2
fun (T x[3]); // fun3
main() {
T a[2];
T b[3];
T c[4];
T *d;
fun(a); // call fun2
fun(b) ; // call fun3
fun(c) ; // call fun1 (see comment at end)
fun(d) ; // call fun1
}
Obviously this doesn't work in c++ because compiler sees
array-of-size-N-of-type-T as pointer-to-type-T in most cases. Enforcing type
checking for arrays has nothing to do with passing the whole array by value.
If you think my description of supposed behavior is too far fetched, or that
arrays are just "smoke and mirror" then consider
void fun (T **x) { cout << "fun1"; }
void fun (T (*x)[2]) { cout << "fun2"; }
void fun (T (*x)[3]) { cout << "fun3"; }
int main(int argc, char *argv[]) {
T (*a)[2];
T (*b)[3];
T (*c)[4];
T **d;
fun(a); // call fun2
fun(b) ; // call fun3
fun((T**)(c)) ; // call fun1 (unsafe in c++)
fun(d) ; // call fun1
}
which compiles and behaves as expected. Note that nothing is passed by
value. What this is saying is that pointer-to-array-of-size-N-of-type-T is a
different type for each value of N. What I am saying is that so far I have
not heard a compelling reason why array-of-size-N-of-type-T should not also
be a different type for each value of N.
Another issue that is closely related to this: There is already a problem in
c++ with type pointer-to-array-of-size-N-of-type-T which I marked as
"unsafe" in my example. Essentially you cannot convert
pointer-to-array-of-size-N-of-type-T to pointer-to-pointer-of-type-T without
using a reinterpret cast. This perhaps is the source of the odd behavior of
reinterpreting array-of-size-N-of-type-T as pointer-to-type-T. Of course,
this whole business would go away (and we get true typed arrays in bargain)
if array-of-size-N-of-type-T is automatically converted to pointer-to-type-T
only if there is no matching type for the specific value of N. 19 2809
"Andrey Tarasevich" <an**************@hotmail.com> wrote in message
news:10*************@news.supernews.com... Leor Zolman wrote: In the case of function parameters, if an array got passed by value,
that would be a very expensive operation. Most of the time folks would have
to program around it, by writing an expression that evaluates to a pointer
of some kind and passing /that/. But believe it or not, if arrays got
passed by value, that would actually be the special case...
That's not really true. Conceptually, arrays are aggregates and in this respect they are not different from structs. Making arrays passable "by value" wouldn't make things more complicated or inconvenient they already are with structs. Folks would have to do the same thing they always did - if you want to pass something large and don't need a copy - pass it "by pointer".
Actually, impossibility to pass arrays by value made things _more_ cumbersome and error prone, because it breaks the invariance of the code with respect to aggregate types hidden behind typedef-names. The following function will accepts its parameters very differently, depending on whether typedef-name 'T' designates array type or struct type
void foo(T agg)
and that's not a good thing.
because of the following:
As I'm fond of saying, arrays are just "smoke and mirrors" anyway;
they're mostly syntactic sugar, and compilers translate array names into
pointers to their first elements (there are a few exceptions, such as when
applying sizeof).
The only reason you can call these contexts (where arrays don't decay into pointers) "exceptions" is that these contexts are apparently relatively rare compared to the contexts where arrays do decay to pointers (especially true for C). But that's a fake reason. Nothing more than an illusion.
From the formal point of view, arrays keeping their "arrayness" is the _normal_ behavior of arrays, while arrays decaying into pointers is the abnormal (exceptional) behavior. It is definitely not correct to say that arrays are just "smoke and mirrors".
That's true even if you don't pass them to a function.
So, given: int a[10]; a[3] = 5;
that last line actually compiles into something like: *(&a[0] + 3) = 5;
Therefore, in a function call such as: func(a);
it makes perfect sense what gets passed is a pointer to the first
element of the array:
func(&a[0]);
and thus there's no information available to the function about the size
of that array.
Well, it is also important to understand that allowing an array to decay to a pointer when passed to a function is a useful trick, which has its own specific purpose. It purpose is to help create functions that can work with arrays of different sizes.
In contexts where the ability to work with differently sized arrays is not needed, it makes more sense to pass arrays by array-typed pointer/reference (i.e. by a pointer/reference of pointer/reference-to-array type). For example, an application that works with, say, 3d geometry and used the following type to represent points in 3D space
typedef int point_t[3];
should pass these points to functions as follows
void foo(const point_t& point)
or
void bar(const int (&point)[3])
not as
void foo(const point_t point) void bar(const int point[3])
Note, that such function declaration are invariant to the possible future change of 'point_t' definition to
typedef struct point_t { int x, y, z; };
-- Best regards, Andrey Tarasevich
I like your reply Andery. But your idea is a bit more revolutionary than
mine. Read my alternative solution of decaying array-to-size-N-of-type-T to
pointer-to-type-T only if there is no matching type for N in the new thread
"Typed Arrays". I won't shy away from some constructive criticism :)
Canonical Latin wrote: ... Type checking and passing by value are two different issues. Consider:
fun1 (T[]); // pointer-to-type-T
fun2 (T[2]); // array-of-size-2-of-type-T
fun3 (T[3]); // array-of-size-3-of-type-T
fun4 (T**); // pointer-to-pointer-to-type-T
fun5 (T*[2]); // pointer-to-array-of-size-2-of-type-T
No, it is an array of 2 'T*'s.
fun6 (T*[3]); // pointer-to-array-of-size-3-of-type-T
No, it is an array of 3 'T*'s.
You probably meant
fun5(T(*)[2])
fun5(T(*)[3]) In none of the cases the whole array is passed by value and this is not the issue. Suppose that c++ actually did have a legitimate type array-of-size-N-of-type-T. Then
But, C++ does have a legitimate type array-of-size-N-of-type-T. Not
fully functional though.
fun (T x[]); // fun1
fun (T x[2]); // fun2
fun (T x[3]); // fun3
main() {
T a[2];
T b[3];
T c[4];
T *d;
fun(a); // call fun2
fun(b) ; // call fun3
fun(c) ; // call fun1 (see comment at end)
fun(d) ; // call fun1
}
Obviously this doesn't work in c++ because compiler sees array-of-size-N-of-type-T as pointer-to-type-T in most cases.
It is not exactly correct. Compiler sees array-of-size-N-of-type-T as
pointer-to-type-T in case of function parameter declaration. The rest
follows. This is specific to situations when one makes syntactical
attempt to pass arrays "by value".
Note, that if you declare your functions as
fun (T (&x)[2]); // fun2
fun (T (&x)[3]); // fun3
the overloading will start working as you describe.
Enforcing type checking for arrays has nothing to do with passing the whole array by value. If you think my description of supposed behavior is too far fetched, or that arrays are just "smoke and mirror" then consider
void fun (T **x) { cout << "fun1"; }
void fun (T (*x)[2]) { cout << "fun2"; }
void fun (T (*x)[3]) { cout << "fun3"; }
int main(int argc, char *argv[]) {
T (*a)[2];
T (*b)[3];
T (*c)[4];
T **d;
fun(a); // call fun2
fun(b) ; // call fun3
fun((T**)(c)) ; // call fun1 (unsafe in c++)
fun(d) ; // call fun1
}
which compiles and behaves as expected. Note that nothing is passed by value. What this is saying is that pointer-to-array-of-size-N-of-type-T is a different type for each value of N. What I am saying is that so far I have not heard a compelling reason why array-of-size-N-of-type-T should not also be a different type for each value of N.
It _is_ a different type for each value of on. In certain contexts this
difference is lost because of array-to-pointer conversion.
Another issue that is closely related to this: There is already a problem in c++ with type pointer-to-array-of-size-N-of-type-T which I marked as "unsafe" in my example. Essentially you cannot convert pointer-to-array-of-size-N-of-type-T to pointer-to-pointer-of-type-T without using a reinterpret cast. This perhaps is the source of the odd behavior of reinterpreting array-of-size-N-of-type-T as pointer-to-type-T. Of course, this whole business would go away (and we get true typed arrays in bargain) if array-of-size-N-of-type-T is automatically converted to pointer-to-type-T only if there is no matching type for the specific value of N.
Hmm.. I don't exactly understand what you are trying to say here.
--
Best regards,
Andrey Tarasevich
"Andrey Tarasevich" <an**************@hotmail.com> wrote in message
news:10*************@news.supernews.com... Canonical Latin wrote: ... Type checking and passing by value are two different issues. Consider:
fun1 (T[]); // pointer-to-type-T fun2 (T[2]); // array-of-size-2-of-type-T fun3 (T[3]); // array-of-size-3-of-type-T fun4 (T**); // pointer-to-pointer-to-type-T fun5 (T*[2]); // pointer-to-array-of-size-2-of-type-T No, it is an array of 2 'T*'s.
fun6 (T*[3]); // pointer-to-array-of-size-3-of-type-T
No, it is an array of 3 'T*'s.
You probably meant
fun5(T(*)[2]) fun5(T(*)[3])
Oops! Yes I did mean that In none of the cases the whole array is passed by value and this is not
the issue. Suppose that c++ actually did have a legitimate type array-of-size-N-of-type-T. Then
But, C++ does have a legitimate type array-of-size-N-of-type-T. Not fully functional though.
fun (T x[]); // fun1 fun (T x[2]); // fun2 fun (T x[3]); // fun3 main() { T a[2]; T b[3]; T c[4];
T *d; fun(a); // call fun2 fun(b) ; // call fun3 fun(c) ; // call fun1 (see comment at end) fun(d) ; // call fun1 }
Obviously this doesn't work in c++ because compiler sees array-of-size-N-of-type-T as pointer-to-type-T in most cases.
It is not exactly correct. Compiler sees array-of-size-N-of-type-T as pointer-to-type-T in case of function parameter declaration. The rest follows. This is specific to situations when one makes syntactical attempt to pass arrays "by value".
Note, that if you declare your functions as
fun (T (&x)[2]); // fun2 fun (T (&x)[3]); // fun3
That is great. It is almost there. The only problem is that now you cannot
have
fun (T x[]); // fun1
and call it with
T xx[4];
fun(xx);
the overloading will start working as you describe.
Enforcing type checking for arrays has nothing to do with passing the whole array by
value. If you think my description of supposed behavior is too far fetched, or
that arrays are just "smoke and mirror" then consider
void fun (T **x) { cout << "fun1"; } void fun (T (*x)[2]) { cout << "fun2"; } void fun (T (*x)[3]) { cout << "fun3"; } int main(int argc, char *argv[]) { T (*a)[2]; T (*b)[3]; T (*c)[4]; T **d; fun(a); // call fun2 fun(b) ; // call fun3 fun((T**)(c)) ; // call fun1 (unsafe in c++) fun(d) ; // call fun1 }
which compiles and behaves as expected. Note that nothing is passed by value. What this is saying is that pointer-to-array-of-size-N-of-type-T
is a different type for each value of N. What I am saying is that so far I
have not heard a compelling reason why array-of-size-N-of-type-T should not
also be a different type for each value of N. It _is_ a different type for each value of on. In certain contexts this difference is lost because of array-to-pointer conversion.
Another issue that is closely related to this: There is already a
problem in c++ with type pointer-to-array-of-size-N-of-type-T which I marked as "unsafe" in my example. Essentially you cannot convert pointer-to-array-of-size-N-of-type-T to pointer-to-pointer-of-type-T
without using a reinterpret cast. This perhaps is the source of the odd behavior
of reinterpreting array-of-size-N-of-type-T as pointer-to-type-T. Of
course, this whole business would go away (and we get true typed arrays in
bargain) if array-of-size-N-of-type-T is automatically converted to
pointer-to-type-T only if there is no matching type for the specific value of N.
Hmm.. I don't exactly understand what you are trying to say here.
I'm not sure I do either, but I try! Here is another attempt: as it is,
array-of-size-N-of-type-T decays to pointer-of-type-T when not argument to
sizeof and as you noted not qualified by &. Lets say I want array type
checking and still want to be able to pass T[N] to a function that does not
specify the N value e.g.
fun(T *x); // funA
fun(T (&x)[2]); // funB
main() {
T x[3] ;
fun(x) ; // funA
}
So the compiler tries to find fun(T (&x)[3]) and since it can't then it
automatically looks for fun(T *x); // funA -- Best regards, Andrey Tarasevich
On Tue, 11 May 2004 22:01:40 GMT, "Canonical Latin" <ja******@hotmail.com>
wrote: Type checking and passing by value are two different issues. Consider:
fun1 (T[]); // pointer-to-type-T
yes. fun2 (T[2]); // array-of-size-2-of-type-T
Now, are you showing what you'd like it to be, or what you think is
actually being declared? As per all that's been said before, the type of
the parameter above is actually the same as the previous: pointer-to-T. fun3 (T[3]); // array-of-size-3-of-type-T
again, just pointer-to-T. fun4 (T**); // pointer-to-pointer-to-type-T
yes. fun5 (T*[2]); // pointer-to-array-of-size-2-of-type-T
Nope, just pointer-to-pointer-to-T. fun6 (T*[3]); // pointer-to-array-of-size-3-of-type-T
Still just pointer-to-pointer-to-T.
In none of the cases the whole array is passed by value and this is not the issue. Suppose that c++ actually did have a legitimate type array-of-size-N-of-type-T. Then
fun (T x[]); // fun1
fun (T x[2]); // fun2
fun (T x[3]); // fun3
main() {
T a[2];
T b[3];
T c[4];
T *d;
fun(a); // call fun2
fun(b) ; // call fun3
fun(c) ; // call fun1 (see comment at end)
fun(d) ; // call fun1
/IF/ C++ did as you'd like, then it would break all pre-existing code that
relies on the age-old behavior of arrays "decaying" to pointers. Imagine
requiring there to be an overload for each possible length N in cases such
as:
char string[N];
...
int i = strlen(string);
Would we want to have to have a separately compiled version of strlen for
each possible value of N? Or for strlen to be a template with a non-type
template parameter N? Yuck. }
Obviously this doesn't work in c++ because compiler sees array-of-size-N-of-type-T as pointer-to-type-T in most cases.
Remember that arrays retain their full type information as long as the
name they were declared with (as arrays) is still in scope and you use it.
That's why sizeof(array) works and &array actually yields a
pointer-to-array. But most of the things you do with arrays, such as
subscripting, end up with you losing that "array-ness" because it is no
longer needed by the resulting expression type. The fact this information
is also lost when using an array as a function argument is just an
occupational hazard of C/C++ programming. Check out today's thread with
subject "Copying struct with array" for some history as to why structure
assignment is allowed while array assignment is not; it's quite related to
this issue.
Enforcing type checking for arrays has nothing to do with passing the whole array by value. If you think my description of supposed behavior is too far fetched, or that arrays are just "smoke and mirror" then consider
It's not that I just /think/ they're smoke and mirrors, I know from having
implemented a C compiler by hand that native C/C++ arrays are essentially
nothing more than notational convenience (and extremely so in the case of
array function parameters). If you want object semantics for something
that works like an array, simply use a vector, or even boost::array if you
don't require dynamic resizing.
Every so often folks suggest how things "should" be in C or C++, and it
isn't that they aren't good ideas, but they're not good enough for the
chaos attempting to retro-fit them to the language /now/ would inevitably
cause. C++ is difficult enough to grok as it stands. I think the entire C++
community is only barely beginning to feel a bit of relief that compilers
in general have /almost/ caught up with the language as it was standardized
in 1998 (and only one family, the one based on the EDG front end, actually
has). Changes to the basic semantics of arrays just aren't going to happen. void fun (T **x) { cout << "fun1"; }
void fun (T (*x)[2]) { cout << "fun2"; }
void fun (T (*x)[3]) { cout << "fun3"; }
int main(int argc, char *argv[]) {
T (*a)[2];
T (*b)[3];
T (*c)[4];
T **d;
fun(a); // call fun2
fun(b) ; // call fun3
fun((T**)(c)) ; // call fun1 (unsafe in c++)
fun(d) ; // call fun1
}
which compiles and behaves as expected. Note that nothing is passed by value. What this is saying is that pointer-to-array-of-size-N-of-type-T is a different type for each value of N. What I am saying is that so far I have not heard a compelling reason why array-of-size-N-of-type-T should not also be a different type for each value of N.
As I said above, even if this would be an overall improvement to the
language (and I'm not saying it would be; I haven't completely thought it
through, and I'm not likely to), it would no longer be C or C++ and thus
could not possibly be worth breaking most of the existing C/C++ code base
to effect.
Note that using boost::array, you can achieve the overloading you want,
retain full performance of native arrays, and still "pass" arrays as
efficiently as C/C++ doesn't (just pass by reference).
Another issue that is closely related to this: There is already a problem in c++ with type pointer-to-array-of-size-N-of-type-T which I marked as "unsafe" in my example. Essentially you cannot convert pointer-to-array-of-size-N-of-type-T to pointer-to-pointer-of-type-T without using a reinterpret cast. This perhaps is the source of the odd behavior of reinterpreting array-of-size-N-of-type-T as pointer-to-type-T. Of course, this whole business would go away (and we get true typed arrays in bargain) if array-of-size-N-of-type-T is automatically converted to pointer-to-type-T only if there is no matching type for the specific value of N.
Passing multi-dimensional arrays around takes some intestinal fortitude. It
always confuses the hell out of /me/, so I can't disagree about the
languages being weak in that regard. To make matters worse, there are no
wonderful replacements for multidimensional arrays in the modern C++ libs,
alas (at least none I've found yet.)
-leor
--
Leor Zolman --- BD Software --- www.bdsoft.com
On-Site Training in C/C++, Java, Perl and Unix
C++ users: download BD Software's free STL Error Message Decryptor at: www.bdsoft.com/tools/stlfilt.html
"Leor Zolman" <le**@bdsoft.com> wrote in message
news:om********************************@4ax.com... On Tue, 11 May 2004 22:01:40 GMT, "Canonical Latin" <ja******@hotmail.com> wrote:
fun4 (T**); // pointer-to-pointer-to-type-T yes.fun5 (T*[2]); // pointer-to-array-of-size-2-of-type-T Nope, just pointer-to-pointer-to-T. fun6 (T*[3]); // pointer-to-array-of-size-3-of-type-T
Still just pointer-to-pointer-to-T.
Granted that I made a mistake in my notation here as Andrey pointed out. The
correct way to say this is
fun (T**); // pointer-to-pointer-to-type-T
fun (T(*)[2]); // pointer-to-array-of-size-2-of-type-T
fun (T(*)[3]); // pointer-to-array-of-size-3-of-type-T
I may be mistaken but from reading your post it seems to me that you are
under the impression that the 3 functions can't be overloaded as above. But
they can. There is a real honest to goodness (lol) type
pointer-to-array-of-size-3-of-type-T that does not decay to anything.
... void fun (T **x) { cout << "fun1"; } void fun (T (*x)[2]) { cout << "fun2"; } void fun (T (*x)[3]) { cout << "fun3"; } int main() { T (*a)[2]; T (*b)[3]; T (*c)[4]; T **d; fun(a); // call fun2 fun(b) ; // call fun3 fun((T**)(c)) ; // call fun1 (unsafe) fun(d) ; // call fun1 } ...
As I said above, even if this would be an overall improvement to the language (and I'm not saying it would be; I haven't completely thought it through, and I'm not likely to), it would no longer be C or C++ and thus could not possibly be worth breaking most of the existing C/C++ code base to effect.
Try compiling the code that I gave above. It really does compile and run
with obvious modifications (e.g. if replace T with say 'int'). Also Andrey
answered my question in a very surprising way. You can coerce the compiler
to do array type checking for you. I just don't see why you have to jump
through hoops to do it. But the following code does exactly what I thought
can't be done--but i'm learning :)
#include<iostream>
void fun(int *&x) {std::cout << "fun1" << std::endl;}
void fun(int (&x)[2]) {std::cout << "fun2" << std::endl;}
void fun(int (&x)[3]) {std::cout << "fun3" << std::endl;}
int main() {
int aa[2];
int bb[3];
int cc[4];
fun(aa); // fun2
fun(bb); // fun3
int *p_cc=cc; // manual decay
fun(p_cc); // fun1
}
Alas you need that "manual decay" :( But on the bright side I qualified endl
with std namespace :)
On Wed, 12 May 2004 00:48:38 GMT, "Canonical Latin"
<ja******@hotmail.com> wrote: It is not exactly correct. Compiler sees array-of-size-N-of-type-T as pointer-to-type-T in case of function parameter declaration. The rest follows. This is specific to situations when one makes syntactical attempt to pass arrays "by value".
Note, that if you declare your functions as
fun (T (&x)[2]); // fun2 fun (T (&x)[3]); // fun3 That is great. It is almost there. The only problem is that now you cannot have fun (T x[]); // fun1 and call it with T xx[4]; fun(xx);
Yes you can. You can have overloads for array ones and pointer ones. Hmm.. I don't exactly understand what you are trying to say here. I'm not sure I do either, but I try! Here is another attempt: as it is, array-of-size-N-of-type-T decays to pointer-of-type-T when not argument to sizeof and as you noted not qualified by &. Lets say I want array type checking and still want to be able to pass T[N] to a function that does not specify the N value e.g. fun(T *x); // funA fun(T (&x)[2]); // funB main() { T x[3] ; fun(x) ; // funA } So the compiler tries to find fun(T (&x)[3]) and since it can't then it automatically looks for fun(T *x); // funA
Yes that will work (once you add the int returns back in - implicit
int return is an old C feature, never a C++ one, although some
compilers have supported it for backwards compatibility reasons).
For the ultimate:
template <int N>
void fun(T (&x)[N])
{
//N gives the size of the array!
}
Tom
--
C++ FAQ: http://www.parashift.com/c++-faq-lite/
C FAQ: http://www.eskimo.com/~scs/C-faq/top.html
On Wed, 12 May 2004 04:51:51 GMT, "Canonical Latin" <ja******@hotmail.com>
wrote: "Leor Zolman" <le**@bdsoft.com> wrote in message news:om********************************@4ax.com.. . On Tue, 11 May 2004 22:01:40 GMT, "Canonical Latin" <ja******@hotmail.com> wrote: > > >fun4 (T**); // pointer-to-pointer-to-type-T yes. >fun5 (T*[2]); // pointer-to-array-of-size-2-of-type-T Nope, just pointer-to-pointer-to-T. > >fun6 (T*[3]); // pointer-to-array-of-size-3-of-type-T Still just pointer-to-pointer-to-T. Granted that I made a mistake in my notation here as Andrey pointed out. The correct way to say this is fun (T**); // pointer-to-pointer-to-type-T fun (T(*)[2]); // pointer-to-array-of-size-2-of-type-T fun (T(*)[3]); // pointer-to-array-of-size-3-of-type-T
And this post was written before Andrey's post came up; it was just delayed
for hours due to a news server glitch at my end, and I eventually re-sent
it.
I may be mistaken but from reading your post it seems to me that you are under the impression that the 3 functions can't be overloaded as above.
I was, and still am, if you're talking about the exact way you posted them
above (as opposed to your corrected version down below). Try compiling this
(and if it works, please tell me what compiler you're using):
#include <iostream>
using namespace std;
typedef int T;
void fun (T x[]) {cout << "fun(T x[])" << endl; }
void fun (T x[2]) {cout << "fun(T x[2])" << endl; }
void fun (T x[3]) {cout << "fun(T x[3])" << endl; }
int main() {
T a[2];
T b[3];
T c[4];
T *d;
fun(a); // call fun2
fun(b) ; // call fun3
fun(c) ; // call fun1 (see comment at end)
fun(d) ; // call fun1
return 0;
}
But they can. There is a real honest to goodness (lol) type pointer-to-array-of-size-3-of-type-T that does not decay to anything.
... > >void fun (T **x) { cout << "fun1"; } >void fun (T (*x)[2]) { cout << "fun2"; } >void fun (T (*x)[3]) { cout << "fun3"; } >int main() { > T (*a)[2]; > T (*b)[3]; > T (*c)[4]; > T **d; > fun(a); // call fun2 > fun(b) ; // call fun3 > fun((T**)(c)) ; // call fun1 (unsafe) > fun(d) ; // call fun1 >} > ... As I said above, even if this would be an overall improvement to the language (and I'm not saying it would be; I haven't completely thought it through, and I'm not likely to), it would no longer be C or C++ and thus could not possibly be worth breaking most of the existing C/C++ code base to effect.
Try compiling the code that I gave above. It really does compile and run with obvious modifications (e.g. if replace T with say 'int').
Yes, I know; by "this" I meant having it allow overloads such as in the
example code I just gave you above (which doesn't compile for me.)
Also Andrey answered my question in a very surprising way. You can coerce the compiler to do array type checking for you. I just don't see why you have to jump through hoops to do it. But the following code does exactly what I thought can't be done--but i'm learning :)
#include<iostream> void fun(int *&x) {std::cout << "fun1" << std::endl;} void fun(int (&x)[2]) {std::cout << "fun2" << std::endl;} void fun(int (&x)[3]) {std::cout << "fun3" << std::endl;} int main() { int aa[2]; int bb[3]; int cc[4]; fun(aa); // fun2 fun(bb); // fun3 int *p_cc=cc; // manual decay fun(p_cc); // fun1 }
Yup, there are certainly ways to do what you're trying to do. The issue I
thought I was discussing was the suggestion that C++ should work
differently than it does when it comes to array names appearing as function
arguments, and the decay that entails. As long as you're not still wanting
that to happen, I've no qualms with any of the rest!
Alas you need that "manual decay" :( But on the bright side I qualified endl with std namespace :)
:-)
-leor
--
Leor Zolman --- BD Software --- www.bdsoft.com
On-Site Training in C/C++, Java, Perl and Unix
C++ users: download BD Software's free STL Error Message Decryptor at: www.bdsoft.com/tools/stlfilt.html
"tom_usenet" <to********@hotmail.com> wrote in message
news:vn********************************@4ax.com... On Wed, 12 May 2004 00:48:38 GMT, "Canonical Latin" <ja******@hotmail.com> wrote:
.... Tom -- C++ FAQ: http://www.parashift.com/c++-faq-lite/ C FAQ: http://www.eskimo.com/~scs/C-faq/top.html
I appreciate the pointers Tom, no pun intended. Just to summarize: My
question was "what is the rational of c++ not having typed arrays in
function calls" and your reply was "But it does if you are careful with
syntax such as:"
template <class T, int N>
void fun(T (&x)[N]) {
std::cout << N << std::endl;
}
Let's go back to function overloading. Surprisingly
int fun (int *x) {} //fun1
int fun (int (&x)[2]){} //fun2
int main() {
int x[2];
fun(x); // ambiguous
}
Does not compile (gcc 3.2.3). It complains about function call being
ambiguous. I don't understand why it is behaving this way, but I humor it
and fix the problem as
int fun (int *&x) {} //fun3
int fun (int (&x)[2]){} //fun2
int main() {
int x[2];
int y[3];
fun(x); // fun2
fun(y); // ambiguos
}
Now the compiler knows how to resolve fun(x) call, but gets stuck on fun(y)
being ambiguous. So I say "huh!" and try various casts on y in fun(y). But
uh-uh it ain't having none of that. So I sit and reason with it and say, hay
look
int fun (int *&x) {} //fun3
int fun (int (&x)[2]){} //fun2
int main() {
int x[2];
int y[3];
fun(x); // fun2
int *p = y;
fun(p); // fun3
}
Now it knows what I'm saying and grudgingly compiles my measly code. But
now, that new variable is a thorn on my side -- if I were looking at someone
else's code I would simply "clean it up". So after my long winded monolog I
come to the question: Suppose I want array type checking and also want
arrays that don't have a matching type to a function to be passed to a
default function (a la example above). Is there a clean way to do this?
Canonical Latin wrote: So after my long winded monolog I come to the question: Suppose I want array type checking and also want arrays that don't have a matching type to a function to be passed to a default function (a la example above). Is there a clean way to do this?
#include <ostream>
#include <iostream>
#include <string>
template <typename T, unsigned N>
void fun (T (& aref) [N])
{
std::cout << "default code\n";
}
template <typename T>
void fun (T (& aref) [3])
{
std::cout << "code for arrays of length 3\n";
}
template <unsigned N>
void fun (double (& aref) [N])
{
std::cout << "code for arrays of double\n";
}
void fun (double (& aref) [3])
{
std::cout << "resolve the ambiguity\n";
}
void test_drive ()
{
double a1 [4];
double a2 [3];
int a3 [4];
std::string a4 [3];
fun (a1);
fun (a2);
fun (a3);
fun (a4);
}
--
Regards,
Buster.
"Buster" <no***@nowhere.com> wrote in message
news:c7**********@newsg4.svr.pol.co.uk... Canonical Latin wrote:
Suppose I want array type checking and also want arrays that don't have a matching type to a function to be passed to a default function (a la example above). Is there a clean way to do this?
#include <ostream> #include <iostream> #include <string>
template <typename T, unsigned N> void fun (T (& aref) [N]) { std::cout << "default code\n"; }
template <typename T> void fun (T (& aref) [3]) { std::cout << "code for arrays of length 3\n"; }
template <unsigned N> void fun (double (& aref) [N]) { std::cout << "code for arrays of double\n"; }
void fun (double (& aref) [3]) { std::cout << "resolve the ambiguity\n"; }
void test_drive () { double a1 [4]; double a2 [3]; int a3 [4]; std::string a4 [3];
fun (a1); fun (a2); fun (a3); fun (a4); }
-- Regards, Buster.
Your solution does work for what I'd asked and I thank you for it. Template
specialization is an interesting solution. However, if there is a solution
without using template for the default function I'm still interested (the
function being default there is no reason to generate identical object code
for each value of N, but that's just me).
Canonical Latin wrote: question was "what is the rational of c++ not having typed arrays in function calls" and your reply was "But it does if you are careful with syntax such as:" template <class T, int N> void fun(T (&x)[N]) { std::cout << N << std::endl; }
Let's go back to function overloading. Surprisingly int fun (int *x) {} //fun1 int fun (int (&x)[2]){} //fun2 int main() { int x[2]; fun(x); // ambiguous } Does not compile (gcc 3.2.3). It complains about function call being ambiguous. I don't understand why it is behaving this way,
It is ambiguous because in C++ the array-to-pointer conversion (required
for the first function) and no-conversion (second function) have the
same rank in the overload resolution process - 'Exact match' (see
13.3.3.1.1/3). In other words, from the compiler's point of view both
functions look equally good in this case.
but I humor it and fix the problem as int fun (int *&x) {} //fun3 int fun (int (&x)[2]){} //fun2 int main() { int x[2]; int y[3]; fun(x); // fun2 fun(y); // ambiguos } Now the compiler knows how to resolve fun(x) call, but gets stuck on fun(y) being ambiguous.
Ambiguous? I don't think it is ambiguous. It is simply impossible.
There's no candidate 'fun' function for 'fun(y)' call. The first
function cannot be called because it requires an lvalue as its argument,
and the result of the array-to-pointer conversion is an rvalue. The
second function simply doesn't match at all.
That's also the reason why the compiler no longer complains about
ambiguity in the 'fun(x)' call. The first function can no longer be used
with arrays.
You can modify the first function declaration to
int fun (int* const& x)
an the second call will compile (resolve to the first function). But the
first will become ambiguous again.
So I say "huh!" and try various casts on y in fun(y). But uh-uh it ain't having none of that. So I sit and reason with it and say, hay look int fun (int *&x) {} //fun3 int fun (int (&x)[2]){} //fun2 int main() { int x[2]; int y[3]; fun(x); // fun2 int *p = y; fun(p); // fun3 } Now it knows what I'm saying and grudgingly compiles my measly code.
Now you have an lvalue ('p'), which the first function can be called with.
But now, that new variable is a thorn on my side -- if I were looking at someone else's code I would simply "clean it up". So after my long winded monolog I come to the question: Suppose I want array type checking and also want arrays that don't have a matching type to a function to be passed to a default function (a la example above). Is there a clean way to do this?
Well, the fact that in overload resolution the array-to-pointer
conversion is given the same rank as no-conversion is one of those many
things that turn arrays into second-class citizens in C++...
--
Best regards,
Andrey Tarasevich
Canonical Latin wrote: However, if there is a solution without using template for the default function I'm still interested (the function being default there is no reason to generate identical object code for each value of N, but that's just me).
template <typename T>
void fun_helper (T * p)
{
std::cout << "default (one per type)\n";
}
template <typename T, unsigned N>
inline void fun (T (& aref) [N])
{
std::cout << "forwarding\n";
fun_helper (aref);
}
--
Regards,
Buster.
"Buster" <no***@nowhere.com> wrote in message
news:c7**********@news6.svr.pol.co.uk... Canonical Latin wrote:
However, if there is a solution without using template for the default function I'm still interested
(the function being default there is no reason to generate identical object
code for each value of N, but that's just me).
template <typename T> void fun_helper (T * p) { std::cout << "default (one per type)\n"; }
template <typename T, unsigned N> inline void fun (T (& aref) [N]) { std::cout << "forwarding\n"; fun_helper (aref); }
-- Regards, Buster.
How I wish that would worke. But "surprisingly"
int fun (int *x) {} //fun1
int fun (int (&x)[2]){} //fun2
int main() {
int x[2];
fun(x); // ambiguous
}
Does not compile (gcc 3.2.3). As Andrey says
"Well, the fact that in overload resolution the array-to-pointer
conversion is given the same rank as no-conversion is one of those many
things that turn arrays into second-class citizens in C++..."
"Andrey Tarasevich" <an**************@hotmail.com> wrote in message
news:10*************@news.supernews.com... Canonical Latin wrote: ... int fun (int *&x) {} //fun3 int fun (int (&x)[2]){} //fun2 int main() { int x[2]; int y[3]; fun(x); // fun2 fun(y); // ambiguos } Now the compiler knows how to resolve fun(x) call, but gets stuck on
fun(y) being ambiguous. Ambiguous? I don't think it is ambiguous. It is simply impossible. There's no candidate 'fun' function for 'fun(y)' call. The first function cannot be called because it requires an lvalue as its argument, and the result of the array-to-pointer conversion is an rvalue. The second function simply doesn't match at all.
Yes I see it now.
... Suppose I want array type checking and also want arrays that don't have a matching type to a function to be passed to a default function (a la example above). Is there a clean way to do this?
Well, the fact that in overload resolution the array-to-pointer conversion is given the same rank as no-conversion is one of those many things that turn arrays into second-class citizens in C++...
-- Best regards, Andrey Tarasevich
Thank you. I did learn quite a bit.
Canonical Latin wrote: "Buster" <no***@nowhere.com> wrotetemplate <typename T> void fun_helper (T * p) { std::cout << "default (one per type)\n"; }
template <typename T, unsigned N> inline void fun (T (& aref) [N]) { std::cout << "forwarding\n"; fun_helper (aref); }
-- Regards, Buster.
How I wish that would worke. But "surprisingly" int fun (int *x) {} //fun1 int fun (int (&x)[2]){} //fun2 int main() { int x[2]; fun(x); // ambiguous }
That's different from what I wrote, which works fine.
--
Regards,
Buster.
"Buster" <no***@nowhere.com> wrote in message
news:c7**********@newsg4.svr.pol.co.uk... Canonical Latin wrote: "Buster" <no***@nowhere.com> wrotetemplate <typename T> void fun_helper (T * p) { std::cout << "default (one per type)\n"; }
template <typename T, unsigned N> inline void fun (T (& aref) [N]) { std::cout << "forwarding\n"; fun_helper (aref); }
-- Regards, Buster.
How I wish that would worke. But "surprisingly" int fun (int *x) {} //fun1 int fun (int (&x)[2]){} //fun2 int main() { int x[2]; fun(x); // ambiguous }
That's different from what I wrote, which works fine.
-- Regards, Buster.
Yes I completely missed it. I really like your solution. Thank you.
"Canonical Latin" <ja******@hotmail.com> wrote in message
news:He*****************@newssvr27.news.prodigy.co m... How I wish that would worke. But "surprisingly" int fun (int *x) {} //fun1 int fun (int (&x)[2]){} //fun2 int main() { int x[2]; fun(x); // ambiguous }
class int_p
{
public:
int_p(int*ptr):ptr_(ptr){}
operator int*()const{ return ptr_; }
private:
int* ptr_;
};
void fun (int_p x) {} //fun1
void fun (int (&x)[2]){} //fun2
int main() {
int x[2];
int y[3];
fun(x); // calls fun2
fun(y); // calls fun1
}
Cheers,
Serge
"Sergiy Kanilo" <sk*****@artannlabs.com> wrote in message
news:9b******************************@news.teranew s.com... "Canonical Latin" <ja******@hotmail.com> wrote in message news:He*****************@newssvr27.news.prodigy.co m...
How I wish that would worke. But "surprisingly" int fun (int *x) {} //fun1 int fun (int (&x)[2]){} //fun2 int main() { int x[2]; fun(x); // ambiguous }
class int_p { public: int_p(int*ptr):ptr_(ptr){} operator int*()const{ return ptr_; } private: int* ptr_; };
void fun (int_p x) {} //fun1 void fun (int (&x)[2]){} //fun2
int main() { int x[2]; int y[3]; fun(x); // calls fun2 fun(y); // calls fun1 }
Cheers, Serge
I have no idea why it does what it does but it does it like a charm!
I'll be studing it for a while :)
Thanks
Let me thank again everyone who replied. I got some wonderful answers. I'm
going to read all of your posts again carefully and when I have digested all
of it, I'll post a summery. This thread has been closed and replies have been disabled. Please start a new discussion. Similar topics
by: Maximus |
last post by:
Hi,
I'd like to know if it's possible to allocate a multidimensional array
during runtime using the new operator. I tried but VC tells me this:
C:\Program Files\Microsoft Visual...
|
by: Till Crueger |
last post by:
Hi,
I have to implement some simple sorting algorithm. I am NOT asking for you
to do my homework, but my question is rather on how to store the integers.
I recall reading once in here that there...
|
by: Canonical Latin |
last post by:
#include<iostream>
int main() {
char buff;
std::cin.getline(buff,3);
std::cin.getline(buff,3);
std::cout << buff << endl;
}
Run at command prompt and input
1234567
what do you get as output?
|
by: Matteo Settenvini |
last post by:
Ok, I'm quite a newbie, so this question may appear silly. I'm using
g++ 3.3.x.
I had been taught that an array isn't a lot different from a pointer
(in fact you can use the pointer arithmetics to...
|
by: Rennie deGraaf |
last post by:
A question regarding this code, which defines a struct containing a size
and a variable-sized array:
typedef struct
{
uint16_t count;
unsigned char bytes;
} foo_t;
....
|
by: rir3760 |
last post by:
Since a few days ago I have been working with the program I post
below (a school assignment). The purpose of the program is to work
with the va_ macros (stdarg.h) and arrays of arrays, hopefully...
|
by: Martin Jørgensen |
last post by:
Hi,
I'm relatively new with C-programming and even though I've read about
pointers and arrays many times, it's a topic that is a little confusing
to me - at least at this moment:
----
1)...
|
by: Sunny |
last post by:
#include <iostream>
int main()
{
int len;
std::cin >len;
int Arr;
int *p = new int;
Arr=5;
std::cout << &len << " " << &Arr << " " << p << std::endl;
|
by: weaknessforcats |
last post by:
Arrays Revealed
Introduction
Arrays are the built-in containers of C and C++. This article assumes the reader has some experiece with
arrays and array syntax but is not clear on a )exactly how...
|
by: Charles Arthur |
last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
|
by: ryjfgjl |
last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
|
by: BarryA |
last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
|
by: Sonnysonu |
last post by:
This is the data of csv file
1 2 3
1 2 3
1 2 3
1 2 3
2 3
2 3
3
the lengths should be different i have to store the data by column-wise with in the specific length.
suppose the i have to...
|
by: Hystou |
last post by:
There are some requirements for setting up RAID:
1. The motherboard and BIOS support RAID configuration.
2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
|
by: Oralloy |
last post by:
Hello folks,
I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>".
The problem is that using the GNU compilers,...
|
by: jinu1996 |
last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
|
by: tracyyun |
last post by:
Dear forum friends,
With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
|
by: agi2029 |
last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...
| |