Type of the array extension in C++

1 day ago 1
ARTICLE AD BOX

the type of N in T[N] is std::size_t.

True, which means the two overloads have to be ambiguous.

Think about the implications of this requirement. Every time an array is declared, there is an implicit conversion of the size to std::size_t. It is as if each valid T[N] is replaced by T[static_cast<std::size_t>(N)], with emphasis on "valid". (If compilation fails without this replacement, the replacement is not done. For example, using a scoped enumeration (enum class) as the size will not compile even though adding the cast would make it compile.) For the question's code, this replacement preserves semantics.

Take another look at the declaration of the last template:

template<int N> void check(char const (&)[N])

What is the type of the parameter? It is a reference to an array. Since it is an array, the size must be of type std::size_t, as you yourself pointed out. The compiler sees this declaration as

template<int N> void check(char const (&)[static_cast<std::size_t>(N)])

This conversion is done before overload resolution sees the function signature. The type of this parameter is the same as the type of the parameter to the other template. Ambiguous.

Note that the same implicit conversion happens in the declaration char c[5], as your first example shows. The literal 5 has type int and gets converted to std::size_t in order to form the type more accurately written char [5uz] (typically char [5ull] pre-C++23). Whenever you see an array type, the size must be std::size_t regardless of the nominal type of the expression giving the size.

JaMiT's user avatar

3 Comments

To match N = size_t(5) (the type and value of the array extension) with the first overload you don't need any implicit conversion of N at all, while you need a conversion from size_t to int for the second. The first overload should win.

2026-02-28T16:29:14.403Z+00:00

I've added more explanation to the answer. Does it make more sense now?

2026-02-28T17:16:18.877Z+00:00

Yes it's. I was thinking on best match in terms of "template parameters", but actually, it's about the function signature, and the function signature is the same in both cases. Got it now.

2026-02-28T17:21:30.627Z+00:00

Both overloads are viable.

Then we have to select the Best viable function

Your concern should be:

there is at least one argument of F1 whose implicit conversion is better than the corresponding implicit conversion for that argument of F2, or, if not that,

Then in Ranking of implicit conversion sequences

Exact match: no conversion required, lvalue-to-rvalue conversion, qualification conversion, function pointer conversion,(since C++17) user-defined conversion of class type to the same class Promotion: integral promotion, floating-point promotion Conversion: integral conversion, floating-point conversion, floating-integral conversion, pointer conversion, pointer-to-member conversion, boolean conversion, user-defined conversion of a derived class to its base

I don't see anything which won't qualify const char(&)[N] (with N either std::size_t nor int) as exact match.

And there are no other tie breaker.

So it is ambiguous.

As a note, they both have same signature, see:

template<std::size_t N> void check(char const (&)[N]) {} template<int N> void check_int(char const (&)[N]) {} static_assert(std::is_same_v<decltype(&check<5>), decltype(&check_int<5>)>); // Pass

Demo

You might also note that conversion ranking concerns function parameters, not conversion to template parameter.

template<std::size_t N> void ambiguous() {} template<int N> void ambiguous() {} int main() { constexpr int n = 5; ambiguous<n>(); // Error constexpr int sz = 42; ambiguous<sz>(); // Error }

Demo

Jarod42's user avatar

4 Comments

But N IS already a size_t by itself, regardless of which overload it's used right? I mean, if check as a function didn't exist at all, N is a size_t realdy. Isn't? Why isn't the first overload an exact match? An if it's not an "size_t" by itself, why in the first example it's resolved as size_t?

2026-02-28T15:46:01.217Z+00:00

Both are exact match. char const (&)[5] -> char const (&)[N] with N=5.

2026-02-28T16:19:38.467Z+00:00

The value of N is the same for both overloads but the type of N is different, and the type of N in the first overload matches exactly the type of N in char[N]. The first overload should win. I still don't understand.

2026-02-28T16:27:54.32Z+00:00

template<int N> void check(char const (&)[N]) is template<int N> void check(char const (&)[static_cast<std::size_t>(N)]), so they have same signature.

2026-02-28T16:45:02.323Z+00:00

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.

Read Entire Article