decltype with C++ array

I have a question about decltype. For the following code:

#include <iostream>
#include <type_traits>

int main()
{
    int a[3];
    std::cout << std::is_same_v<decltype((a)), int(&)[3]> << std::endl;
    std::cout << std::is_same_v<decltype(a + 0), int*> << std::endl;
}

All of the outputs are 1. But as cppreference said:

If the argument is any other expression of type T, and if the value category of expression is lvalue (or pvalue), then decltype yields T& (or T);

I don’t understand why decltype((a)) is int(&)[3], since

  1. a is an array variable, which is not an lvalue

  2. (a) is an expression, so I think decltype((a)) should return its type, which should be decayed as int*, as the second output shows.

So basically, I don’t understand the difference between (a) and a + 0 when using decltype.

Answer

a is an array variable, which is not a lvalue

a is both a variable name and an expression. Variables themselves don’t have value categories, but when used as an expression, it’s an lvalue.

(a) is an expression, completely equivalent to expression a. (a) is not a variable name, obviosuly.

I think decltype((a)) should return its type

As you quote says, for expressions decltype also encodes their value category in the type it returns, by adding &, &&, or nothing to it. Since (a) is an lvalue, & is added.

should be decayed as int*

Array-to-pointer decay doesn’t always happen. It happens during most uses of arrays, but not all of them.

In particular, it doesn’t happen when passing an array to decltype.

as the second output shows

In the second case, applying + to the array forces its decay to a pointer.

The resulting pointer is a prvalue, so the reported type is not a reference.