7

I'm interested to know why the second static_assert in my code below doesn't work. It seems like even though the array c is a reference to a, the size of the array is embedded in the type, so it should be available at compile time.

#include <array>

int main()
{
    std::array<int,2> a = {1,2};
    std::array<int,2> b = {2,3};
    std::array<int,2>& c = a;

    static_assert(a.size() == b.size(), "a.size==b.size"); // ok size of array is compile time constant
    static_assert(c.size() == a.size(), "c.size==a.size"); // compiler error "static_assert expression is not an integral constant expression"
}
anastaciu
  • 23,467
  • 7
  • 28
  • 53
Jacob Merson
  • 130
  • 5

2 Answers2

8

the size of the array is embedded in the type, so it should be available at compile time.

This is true. But regardless, c is not a constant expression and therefore expression that contains it as a subexpression cannot be a constant expression - except certain operators that interact only with the type of the operand such as sizeof.

You can get the size for example using:

static_assert(
    std::tuple_size<
        std::remove_reference_t<decltype(c)>
    >::value == a.size(),
    "c.size==a.size"
);

Unfortunately, it is not very pretty.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • could probably wrap it into a derived template.. Tuple_size is nothing more than `integral_constant` which uses `sizeof()`, so straight-forward `sizeof()` idiom works too, if `tuple_size` isn't included into implementation (it sometimes overlooked). – Swift - Friday Pie Aug 09 '20 at 22:01
3

Note, that if you move all declaration out of main function, code would compile. Why? Because a is automatic variable, so it's not really a compile time object and reference is not elided here, therefore neither a nor cor c.size() are constexpr. For global variables location of a can be determined at compile time.

IF you try bind them within function like this:

constexpr std::array<int,2> a = {1,2};
std::array<int,2> b = {2,3};
constexpr const std::array<int,2>& c = a;

You'll get error that a is not a constant expression. Variant which still may compile:

#include <array>

std::array<int,2> a = {1,2};
std::array<int,2> b = {2,3};

int main()
{
    std::array<int,2>& c = a;

    static_assert(a.size() == b.size(), "a.size==b.size"); 
    static_assert(c.size() == a.size(), "c.size==a.size"); 
}
Swift - Friday Pie
  • 12,777
  • 2
  • 19
  • 42
  • 1
    This behaviour just goes to show how ridiculous C++ can be. – Paul Sanders Aug 09 '20 at 21:55
  • @PaulSanders Russel's paradox sound ridiculous too, but it is logical. We had created a run-time alias for "something" which was created run-time, but we ask question about properties known in compile time, using function `sizeof()` call to which is sensitive to nature of object it is invoked with. The is no logical way to formulate rules how compiler have to divine that we ask about something it can deduce. – Swift - Friday Pie Aug 09 '20 at 22:05
  • `size()`, not `sizeof()`, that's a typo – Swift - Friday Pie Aug 09 '20 at 22:25