Li Chen
Li Chen

Reputation: 5270

As template type argument, why doesn't type[N] match its specialized version ---- template<class T> class S<T[]>

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
}

demo


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

Answers (1)

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

Related Questions