Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.c++ > #123494 > unrolled thread
| Started by | Andrey Tarasevich <noone@noone.net> |
|---|---|
| First post | 2026-02-25 08:08 -0800 |
| Last post | 2026-03-02 21:34 -0800 |
| Articles | 9 — 5 participants |
Back to article view | Back to comp.lang.c++
Array size validity and SFINAE Andrey Tarasevich <noone@noone.net> - 2026-02-25 08:08 -0800
Re: Array size validity and SFINAE Marcel Mueller <news.5.maazl@spamgourmet.org> - 2026-03-01 16:55 +0100
Re: Array size validity and SFINAE Andrey Tarasevich <noone@noone.net> - 2026-03-01 08:23 -0800
Re: Array size validity and SFINAE "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> - 2026-03-01 16:36 -0800
Re: Array size validity and SFINAE Andrey Tarasevich <noone@noone.net> - 2026-03-01 16:45 -0800
Re: Array size validity and SFINAE Bonita Montero <Bonita.Montero@gmail.com> - 2026-03-02 13:21 +0100
Re: Array size validity and SFINAE Tim Rentsch <tr.17687@z991.linuxsc.com> - 2026-03-02 00:38 -0800
Re: Array size validity and SFINAE Andrey Tarasevich <noone@noone.net> - 2026-03-02 07:48 -0800
Re: Array size validity and SFINAE Tim Rentsch <tr.17687@z991.linuxsc.com> - 2026-03-02 21:34 -0800
| From | Andrey Tarasevich <noone@noone.net> |
|---|---|
| Date | 2026-02-25 08:08 -0800 |
| Subject | Array size validity and SFINAE |
| Message-ID | <10nn6pp$su58$1@dont-email.me> |
#include <iostream>
struct S { static constexpr int N = -1; };
template <typename T> void foo(int [T::N])
{
std::cout << "array" << std::endl;
}
template <typename T> void foo(...)
{
std::cout << "..." << std::endl;
}
int main()
{
foo<int>(0);
}
GCC outputs "array", Clang and MSVC output "..."
The struct definition can be changed to `struct S {};` with the same
behavior from GCC. I.e. in template contexts GCC does not check the
validity of the size specified in array parameter declaration: it
doesn't care whether it is positive, it doesn't even care whether the
nested name exists. One can guess that this is most likely a consequence
of the array parameter type adjustment, which makes the size essentially
"disappear". However, it still does not necessarily mean that such array
parameter declaration should be accepted as valid.
So, who's right here standard-wise?
--
Best regards,
Andrey
[toc] | [next] | [standalone]
| From | Marcel Mueller <news.5.maazl@spamgourmet.org> |
|---|---|
| Date | 2026-03-01 16:55 +0100 |
| Message-ID | <10o1ngo$8go3$3@gwaiyur.mb-net.net> |
| In reply to | #123494 |
Am 25.02.26 um 17:08 schrieb Andrey Tarasevich:
> #include <iostream>
>
> struct S { static constexpr int N = -1; };
>
> template <typename T> void foo(int [T::N])
> {
> std::cout << "array" << std::endl;
> }
>
> template <typename T> void foo(...)
> {
> std::cout << "..." << std::endl;
> }
>
> int main()
> {
> foo<int>(0);
> }
>
> GCC outputs "array", Clang and MSVC output "..."
You are taking an array by value. This might not be the intention and
might take a slice. Since the array index is unsigned -1 is UB or in
fact expand to UINT_MAX.
If you write
template <typename T> void foo(int (&)[T::N])
gcc will also output ...
I am unsure whether the overload resolution should take place before the
other checks.
Marcel
[toc] | [prev] | [next] | [standalone]
| From | Andrey Tarasevich <noone@noone.net> |
|---|---|
| Date | 2026-03-01 08:23 -0800 |
| Message-ID | <10o1p6d$cadm$1@dont-email.me> |
| In reply to | #123499 |
On Sun 3/1/2026 7:55 AM, Marcel Mueller wrote:
>> #include <iostream>
>>
>> struct S { static constexpr int N = -1; };
>>
>> template <typename T> void foo(int [T::N])
>> {
>> std::cout << "array" << std::endl;
>> }
>>
>> template <typename T> void foo(...)
>> {
>> std::cout << "..." << std::endl;
>> }
>>
>> int main()
>> {
>> foo<int>(0);
>> }
>>
>> GCC outputs "array", Clang and MSVC output "..."
>
> You are taking an array by value.
There's no such thing as "taking an array by value". Neither in C nor in
C++.
> This might not be the intention and
> might take a slice.
???
> Since the array index is unsigned -1 is UB or in
> fact expand to UINT_MAX.
Um... My question is intended for people who have "language lawyer"
level of understanding the language. You are obviously not quite there yet.
At some point in the future you you might come across the concept
colloquially known as SFINAE (which is mentioned in the subject of my
question). Once you learn what it is (if ever), you will understand what
this question is about.
--
Best regards,
Andrey
[toc] | [prev] | [next] | [standalone]
| From | "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> |
|---|---|
| Date | 2026-03-01 16:36 -0800 |
| Message-ID | <10o2m1i$n2lg$1@dont-email.me> |
| In reply to | #123500 |
On 3/1/2026 8:23 AM, Andrey Tarasevich wrote:
> On Sun 3/1/2026 7:55 AM, Marcel Mueller wrote:
>>> #include <iostream>
>>>
>>> struct S { static constexpr int N = -1; };
>>>
>>> template <typename T> void foo(int [T::N])
>>> {
>>> std::cout << "array" << std::endl;
>>> }
>>>
>>> template <typename T> void foo(...)
>>> {
>>> std::cout << "..." << std::endl;
>>> }
>>>
>>> int main()
>>> {
>>> foo<int>(0);
>>> }
>>>
>>> GCC outputs "array", Clang and MSVC output "..."
>>
>> You are taking an array by value.
>
> There's no such thing as "taking an array by value". Neither in C nor in
> C++.
pseudo-code sorry for any typos... Not your main point, but
struct foo
{
int xxx[10];
};
void
bar(struct foo bar)
{
bar.xxx[0] = 123;
}
>
>> This might not be the intention and might take a slice.
>
> ???
>
>> Since the array index is unsigned -1 is UB or in fact expand to UINT_MAX.
>
> Um... My question is intended for people who have "language lawyer"
> level of understanding the language. You are obviously not quite there yet.
>
> At some point in the future you you might come across the concept
> colloquially known as SFINAE (which is mentioned in the subject of my
> question). Once you learn what it is (if ever), you will understand what
> this question is about.
>
[toc] | [prev] | [next] | [standalone]
| From | Andrey Tarasevich <noone@noone.net> |
|---|---|
| Date | 2026-03-01 16:45 -0800 |
| Message-ID | <10o2mk5$n9al$1@dont-email.me> |
| In reply to | #123501 |
On Sun 3/1/2026 4:36 PM, Chris M. Thomasson wrote: >> >> There's no such thing as "taking an array by value". Neither in C nor >> in C++. > > pseudo-code sorry for any typos... Not your main point, but > With the same success you could've posted an example passing a `std::array` by value. Not relevant. -- Best regards, Andrey
[toc] | [prev] | [next] | [standalone]
| From | Bonita Montero <Bonita.Montero@gmail.com> |
|---|---|
| Date | 2026-03-02 13:21 +0100 |
| Message-ID | <10o3vc1$14fgl$1@raubtier-asyl.eternal-september.org> |
| In reply to | #123499 |
Am 01.03.2026 um 16:55 schrieb Marcel Mueller: > You are taking an array by value. This might not be the intention and > might take a slice. Since the array index is unsigned -1 is UB or in > fact expand to UINT_MAX. > If you write > template <typename T> void foo(int (&)[T::N]) > gcc will also output ... > I am unsure whether the overload resolution should take place > before the other checks. Or better a C++ span with a fixed size.With that you can have bouds-checking wile debugging.
[toc] | [prev] | [next] | [standalone]
| From | Tim Rentsch <tr.17687@z991.linuxsc.com> |
|---|---|
| Date | 2026-03-02 00:38 -0800 |
| Message-ID | <86zf4qeicj.fsf@linuxsc.com> |
| In reply to | #123494 |
Andrey Tarasevich <noone@noone.net> writes:
> #include <iostream>
>
> struct S { static constexpr int N = -1; };
>
> template <typename T> void foo(int [T::N])
> {
> std::cout << "array" << std::endl;
> }
>
> template <typename T> void foo(...)
> {
> std::cout << "..." << std::endl;
> }
>
> int main()
> {
> foo<int>(0);
> }
>
> GCC outputs "array", Clang and MSVC output "..."
>
> The struct definition can be changed to `struct S {};` with the same
> behavior from GCC. I.e. in template contexts GCC does not check the
> validity of the size specified in array parameter declaration: it
> doesn't care whether it is positive, it doesn't even care whether the
> nested name exists. One can guess that this is most likely a
> consequence of the array parameter type adjustment, which makes the
> size essentially "disappear". However, it still does not necessarily
> mean that such array parameter declaration should be accepted as
> valid.
>
> So, who's right here standard-wise?
I certainly don't claim to be a C++ language lawyer; at best I
might agree to be termed a language paralegal. That said, here are
my thoughts.
First, the struct S is irrelevant; it has no bearing on the
question or the observed behavior.
Second, clang is right, and gcc is wrong. The output under gcc of
"array" would seem to imply that int::N exists, but it doesn't.
Third, if we add a statement
int fred = T::N;
to the first template, gcc gives an error, whereas clang still
outputs '...'. My understanding of SFINAE matches what clang
does, and does not match what gcc does.
Fourth, if after adding 'int fred = ...' to the first template, we
change the call in main() to
foo<S>(0)
then gcc again outputs 'array', whereas clang outputs '...'.
Fifth, if after adding 'int fred = ...' to the first template, we
define a new struct
struct U { static constexpr unsigned int N = -1; };
and change the invocation in main() to
foo<U>(0)
then both gcc and clang both output 'array'.
Incidentally, trying to define an analogous pair of overloaded
functions simply rejects the definition with a negative value for
the extent of the array parameter. That holds for both gcc and
clang.
In every case clang does what I expect, and is consistent with what
I understand about SFINAE. I can't say the same for gcc. So,
either my understanding of SFINAE is messed up, or gcc is confused
about something.
[toc] | [prev] | [next] | [standalone]
| From | Andrey Tarasevich <noone@noone.net> |
|---|---|
| Date | 2026-03-02 07:48 -0800 |
| Message-ID | <10o4bgp$18opb$1@dont-email.me> |
| In reply to | #123505 |
On Mon 3/2/2026 12:38 AM, Tim Rentsch wrote:
>
> First, the struct S is irrelevant; it has no bearing on the
> question or the observed behavior.
You are right. However, the `foo<int>` call in my code is a remnant of
an experiment with non-existing nested name. It also illustrates the
problem.
Yet my original intent was to use
foo<S>();
call.
> Third, if we add a statement
>
> int fred = T::N;
>
> to the first template, gcc gives an error, whereas clang still
> outputs '...'.
Add where exactly? Into the body of the function template? That would
immediately take us out of SFINAE territory and make it a hard error in
cases where `T::N` does not exist. Not surprisingly, any compiler that
chooses the first overload will subsequently issue an error.
> Fourth, if after adding 'int fred = ...' to the first template, we
> change the call in main() to
>
> foo<S>(0)
>
> then gcc again outputs 'array', whereas clang outputs '...'.
That is another facet of the problem I mentioned in my original
question, since array declaration with negative array size is supposed
to be invalid.
Not sure what importance you assign to that "after adding..." part,
since adding 'int fred = ...' to the first template is completely beside
the point in this case.
> Fifth, if after adding 'int fred = ...' to the first template, we
> define a new struct
>
> struct U { static constexpr unsigned int N = -1; };
>
> and change the invocation in main() to
>
> foo<U>(0)
>
> then both gcc and clang both output 'array'.
This is expected, of course. Again, adding 'int fred = ...' to the first
template has no bearing on it.
> Incidentally, trying to define an analogous pair of overloaded
> functions simply rejects the definition with a negative value for
> the extent of the array parameter. That holds for both gcc and
> clang.
Yes, in ordinary functions, as opposed to function templates, GCC does
not ignore any issues with array sizes in parameter declarations, which
is the reason I specifically mentioned that the issue is only visible in
template context.
--
Best regards,
Andrey
[toc] | [prev] | [next] | [standalone]
| From | Tim Rentsch <tr.17687@z991.linuxsc.com> |
|---|---|
| Date | 2026-03-02 21:34 -0800 |
| Message-ID | <86ecm1easg.fsf@linuxsc.com> |
| In reply to | #123507 |
Andrey Tarasevich <noone@noone.net> writes:
> On Mon 3/2/2026 12:38 AM, Tim Rentsch wrote:
>
>> First, the struct S is irrelevant; it has no bearing on the
>> question or the observed behavior.
>
> You are right. However, the `foo<int>` call in my code is a remnant of
> an experiment with non-existing nested name. It also illustrates the
> problem.
>
> Yet my original intent was to use
>
> foo<S>();
>
> call.
>
>> Third, if we add a statement
>>
>> int fred = T::N;
>>
>> to the first template, gcc gives an error, whereas clang still
>> outputs '...'.
>
> Add where exactly? Into the body of the function template?
Yes, just before the std::cout line.
> That would
> immediately take us out of SFINAE territory and make it a hard error
> in cases where `T::N` does not exist.
Oh. I still think of that as SFINAE, since clang does the right
thing.
> Not surprisingly, any compiler
> that chooses the first overload will subsequently issue an error.
Right.
>> Fourth, if after adding 'int fred = ...' to the first template, we
>> change the call in main() to
>>
>> foo<S>(0)
>>
>> then gcc again outputs 'array', whereas clang outputs '...'.
>
> That is another facet of the problem I mentioned in my original
> question, since array declaration with negative array size is supposed
> to be invalid.
>
> Not sure what importance you assign to that "after adding..." part,
> since adding 'int fred = ...' to the first template is completely
> beside the point in this case.
It's a way of showing gcc is confused, since there is another
template that doesn't have an error, which clang correctly
recognizes.
>> Fifth, if after adding 'int fred = ...' to the first template, we
>> define a new struct
>>
>> struct U { static constexpr unsigned int N = -1; };
>>
>> and change the invocation in main() to
>>
>> foo<U>(0)
>>
>> then both gcc and clang both output 'array'.
>
> This is expected, of course. Again, adding 'int fred = ...' to the
> first template has no bearing on it.
Yes, expected. The point of having 'int fred = ...' is to
emphasize the distinction between gcc and clang considering
the pair of invocations foo<S>(0) and foo<U>(0).
>> Incidentally, trying to define an analogous pair of overloaded
>> functions simply rejects the definition with a negative value for
>> the extent of the array parameter. That holds for both gcc and
>> clang.
>
> Yes, in ordinary functions, as opposed to function templates, GCC does
> not ignore any issues with array sizes in parameter declarations,
> which is the reason I specifically mentioned that the issue is only
> visible in template context.
I didn't really think any of my reporting would surprise you. I
included everything in the interest of completeness and to
illustrate my thought processes.
After seeing your reply I tried one more test, changing the first
template to
template <typename T> void foo(int (*p)[T::N])
{
std::cout << "array of size " << sizeof *p << std::endl;
}
in which case the invocation
foo<S>(0);
produces '...' under both gcc and clang.
[toc] | [prev] | [standalone]
Back to top | Article view | comp.lang.c++
csiph-web