Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++: Why is this constexpr not a compile time constant

In the following C++11 code, the last call to arraySize causes a compile error. Apparently this is because y is a runtime sized array, and the arraySize template parameter N cannot be deduced for y. I don't understand why x is a compile time sized array, but y ends up runtime sized. The arraySize template function is taken directly from Scott Meyers' "Effective Modern C++" Item 1.

#include <cstddef>

template<typename T, std::size_t N>
constexpr std::size_t arraySize(T(&)[N]) noexcept { return N; }

struct S
{
    char c[10];
};

int main()
{
    S s;
    S* ps = &s;

    char x[arraySize(s.c)];
    char y[arraySize(ps->c)]; // why is y a runtime sized array?

    arraySize(x);
    arraySize(y); // error !?

    return 0;
}
like image 608
timmah2014 Avatar asked Jan 22 '15 01:01

timmah2014


1 Answers

In C++, the error is not the call to arraySize(y), but the declaration of y itself.

The bounds in an array declaration must be a "converted constant expression".

If your compiler accepts the declaration of y and later tells you that y is an array of runtime bound, it is not a C++ compiler. There are no arrays of runtime bound in any ratified version of C++, nor the current draft.

The significant difference between arraySize(s.c) and arraySize(ps->c) is that ps->c is the same as (*ps).c and the * dereference operator requires lvalue-to-rvalue conversion on ps, which is not a constant expression (nor is &s, see below). The rest of the expression doesn't involve lvalue-to-rvalue conversion, the array lvalue is directly bound by the reference.

A constant expression is either a glvalue core constant expression whose value refers to an entity that is a permitted result of a constant expression (as defined below), or a prvalue core constant expression whose value is an object where, for that object and its subobjects:

  • each non-static data member of reference type refers to an entity that is a permitted result of a constant expression, and

  • if the object or subobject is of pointer type, it contains the address of an object with static storage duration, the address past the end of such an object (5.7), the address of a function, or a null pointer value.

An entity is a permitted result of a constant expression if it is an object with static storage duration that is either not a temporary object or is a temporary object whose value satisfies the above constraints, or it is a function.

Clearly ps contains the address of an object with automatic storage duration, so it cannot be declared constexpr. But everything should start working if you change S s; S* ps = &s; to static S s; constexpr S* ps = &s;

(On the other hand, you'd think that the parameter to arraySize(s.c) is not a constant expression either, since it is a reference and not to an object of static storage duration)

like image 145
Ben Voigt Avatar answered Oct 31 '22 08:10

Ben Voigt