Reputation: 5270
Here is a demo (shortened from cppreference):
#include <iostream>
#include <type_traits>
template<class T>
struct is_array : std::false_type {};
template<class T>
struct is_array<T[]> : std::true_type {};
class A {};
int main()
{
std::cout << std::boolalpha;
std::cout << is_array<A>::value << '\n';
std::cout << is_array<A[]>::value << '\n';
std::cout << is_array<A[3]>::value << '\n';
}
output(live demo
):
false
true
false
We can see that is_array<A[3]>
is resolved as the primary specialization: template<class T> struct is_array
, and not template<class T> struct is_array<T[]>
.
It confuses me a fair bit. Of course I know the complete implementation of std::is_array
(as documented in cppreference) also contains a specialization for length: template<class T, std::size_t N> struct is_array<T[N]>
, and std::is_array<A[3]>
will resolve to this specialization as expected. But that cannot explain the uppermost demo, right?
After searching, I found this thread, but it is a question about how, not why. But in a comment, @Steve Jessop mentioned:
I'm not entirely sure, but I think what you've written there is a specialization for arrays of unknown size. That's an incomplete type, but can be used in an extern declaration of an array that some other TU will provide.
It seems to related to it being an incomplete type:
The declared type of an array object might be an array of unknown bound and therefore be incomplete at one point in a translation unit and complete later on; the array types at those two points (“array of unknown bound of T” and “array of N T”) are different types. The type of a pointer to array of unknown bound, or of a type defined by a typedef declaration to be an array of unknown bound, cannot be completed
extern int arr[]; // the type of arr is incomplete
int arr[10]; // now the type of arr is complete
[basic.types]/6 doesn't offer any other useful information about this.
I noticed an error in the sample from the spec, which got me thinking that maybe it is not about specialization, but because A[3]
cannot match struct is_array<T[]>
. That's what the compiler seems to affirm:
#include <iostream>
#include <type_traits>
template<class T>
struct is_array<T[]> : std::true_type {};
class A {};
int main()
{
is_array<A[3]> *a; // error
is_array<A[3]> b; // error
}
Above is what I tried to solve this by myself, without success. So I am here to find a thorough answer.
Upvotes: 3
Views: 94
Reputation: 170202
The short answer is in the paragraph you quoted yourself:
The declared type of an array object might be an array of unknown bound and therefore be incomplete at one point in a translation unit and complete later on; the array types at those two points (“array of unknown bound of T” and “array of N T”) are different types. The type of a pointer to array of unknown bound, or of a type defined by a typedef declaration to be an array of unknown bound, cannot be completed
Those are different types. While you may "fix" the type of an array object after the first point of declaration, it still doesn't change the fact those are different types. They behave differently. For instance:
using AI = int[];
using AC = int[3];
AI a1 = {1, 2, 3, 4};
AC a2 = {1, 2, 3, 4}; // error
The object a1
can be initialized with the initializer {1, 2, 3, 4}
. We see that the type I aliased as AI
kinda behaves like auto
, an initializer can be used to help deduce the complete type.
On the other hand, a2
causes an error, because I provided too many initializers. And if I were to provide too few, I'd still have 3 elements in my array, where the ones not explicitly initialized will be default initialized.
I hope you are convinced by now those are indeed very different types. So as template arguments, they shouldn't match.
Upvotes: 3