When deducing a non-type template parameter, say n
, the types of n
must match exactly between the parameter and argument. So the following code will not compile (at least on GCC and clang):
#include <array>
template <int n> void f(std::array<int, n> const&) {}
int main() {
std::array<int, 3> arr;
f(arr);
}
This is because std::array
is declared as
template <typename T, std::size_t n> class array;
rather than
template <typename T, int n> class array;
However, to capture the size of a built-in array, it seems that any integral type does. All of the following work on GCC, clang, and VC++:
template <typename T, char n > void f(T (&)[n]);
template <typename T, short n> void f(T (&)[n]);
template <typename T, int n> void f(T (&)[n]);
...
So, seriously, the type of a built-in array's size is overloaded?
From §8.3.4 [dcl.array]/1:
If the constant-expression (5.19) is present, it shall be a converted constant expression of type std::size_t and its value shall be greater than zero. The constant expression specifies the bound of (number of elements in) the array
The type is std::size_t
.
The reason other types work in general is that conversions are done on the type passed in according to §14.3.2 [temp.arg.nontype]/5:
For a non-type template-parameter of integral or enumeration type, conversions permitted in a converted constant expression (5.19) are applied.
From §5.19 [expr.const]/3:
An integral constant expression is an expression of integral or unscoped enumeration type, implicitly converted to a prvalue, where the converted expression is a core constant expression.
snip (mentions these can be used as array bounds)
A converted constant expression of type T is an expression, implicitly converted to a prvalue of type T, where the converted expression is a core constant expression and the implicit conversion sequence contains only user-defined conversions, lvalue-to-rvalue conversions (4.1), integral promotions (4.5), and integral conversions (4.7) other than narrowing conversions (8.5.4).
Finally, §4.7 [conv.integral]/3:
If the destination type is signed, the value is unchanged if it can be represented in the destination type (and bit-field width); otherwise, the value is implementation-defined.
If the value of the bound fits inside of your parameter type, it will be converted successfully. If not, you'll end up with an implementation-defined value for the bound.
Arrays are a special case, as pointed out in ectamur's answer:
§14.8.2.5 [temp.deduct.type]/17:
If, in the declaration of a function template with a non-type template-parameter, the non-type template-parameter is used in an expression in the function parameter-list and, if the corresponding template-argument is deduced, the template-argument type shall match the type of the template-parameter exactly, except that a template-argument deduced from an array bound may be of any integral type.
This is covered in 14.8.2.5 [temp.deduct.type] paragraph 17:
17 - [...] [The] template-argument type shall match the type of the template-parameter exactly, except that a template-argument deduced from an array bound may be of any integral type.
In http://wg21.cmeerw.net/cwg/issue1770 this is improved to the more general:
17 - If
P
has a form that contains<i>
, and if the type of the corresponding value ofA
differs from the type ofi
, deduction fails. IfP
has a form that contains[i]
, and if the type ofi
is not an integral type, deduction fails.
So array bounds can be deduced to any integral type, but non-type template parameters must be deduced to the actual type in the template definition.
Indeed, there is nowhere in the C++11 version of the Standard that specifies a preferred type for array bounds; an array bound is specified (in [dcl.array]) to be "an integral constant expression and its value shall be greater than zero". In recent drafts for C++14 this is amended to "a converted constant expression (5.19) of type std::size_t
[...]"; the changed definition can be traced to n3306. Somewhat oddly, the change is presented as a "consequential adjustment [..] for the sake of consistency", implying that the editors considered it self-apparent that size_t
was the correct type.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With