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.
18.9k5 gold badges20 silver badges44 bronze badges
3 Comments
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 baseI 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>)>); // PassYou 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 }230k15 gold badges215 silver badges364 bronze badges
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
Explore related questions
See similar questions with these tags.



