Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c++ > #123494 > unrolled thread

Array size validity and SFINAE

Started byAndrey Tarasevich <noone@noone.net>
First post2026-02-25 08:08 -0800
Last post2026-03-02 21:34 -0800
Articles 9 — 5 participants

Back to article view | Back to comp.lang.c++


Contents

  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

#123494 — Array size validity and SFINAE

FromAndrey Tarasevich <noone@noone.net>
Date2026-02-25 08:08 -0800
SubjectArray 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]


#123499

FromMarcel Mueller <news.5.maazl@spamgourmet.org>
Date2026-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]


#123500

FromAndrey Tarasevich <noone@noone.net>
Date2026-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]


#123501

From"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>
Date2026-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]


#123502

FromAndrey Tarasevich <noone@noone.net>
Date2026-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]


#123506

FromBonita Montero <Bonita.Montero@gmail.com>
Date2026-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]


#123505

FromTim Rentsch <tr.17687@z991.linuxsc.com>
Date2026-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]


#123507

FromAndrey Tarasevich <noone@noone.net>
Date2026-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]


#123509

FromTim Rentsch <tr.17687@z991.linuxsc.com>
Date2026-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