helloworld922
helloworld922

Reputation: 10939

Constexpr static member function usage

Consider the following example code:

#include <array>

struct MyClass
{
  size_t value = 0;

  constexpr static size_t size() noexcept
  {
    return 3;
  }
};

template <size_t N>
void DoIt()
{
  MyClass h;
  std::array<int, h.size()> arr;
}

int main()
{
  DoIt<1>();
}

When I try to compile this with GCC 7.3.0, I get an error about h not being usable in a non-constexpr context:

cexpr.cpp: In function ‘void DoIt()’:
cexpr.cpp:17:26: error: the value of ‘h’ is not usable in a constant expression
   std::array<int, h.size()> arr;
                          ^
cexpr.cpp:16:11: note: ‘h’ was not declared ‘constexpr’
   MyClass h;
           ^
cexpr.cpp:17:27: error: the value of ‘h’ is not usable in a constant expression
   std::array<int, h.size()> arr;
                           ^
cexpr.cpp:16:11: note: ‘h’ was not declared ‘constexpr’
   MyClass h;
           ^

However, when I try compiling the exact same code in Clang 6.0.0, it compiles without any errors. Additionally, when I modify the code to not be inside the templated DoIt() function, GCC compiles this just fine:

#include <array>

struct MyClass
{
  size_t value = 0;

  constexpr static size_t size() noexcept
  {
    return 3;
  }
};

int main()
{
  MyClass h;
  // this compiles just fine in Clang and GCC
  std::array<int, h.size()> arr;
}

I already know how to fix the first code so it compiles on GCC using decltype, but I'm curious to know why doesn't the first piece of code compile with GCC? Is this just a bug in GCC, or is there something I don't understand about using constexpr static member functions?

Upvotes: 20

Views: 575

Answers (1)

YSC
YSC

Reputation: 40060

Looks like a bug to me.

The type and meaning of the expression h.size() is defined by [expr.ref] "Class member access":

[expr.post]/3

Abbreviating postfix-expression.id-expression as E1.E2, E1 is called the object expression. [...]

and

[expr.post]/6.3.1

If E2 is a (possibly overloaded) member function, function overload resolution is used to determine whether E1.E2 refers to a static or a non-static member function.

  • (6.3.1) If it refers to a static member function and the type of E2 is “function of parameter-type-list returning T”, then E1.E2 is an lvalue; the expression designates the static member function. The type of E1.E2 is the same type as that of E2, namely “function of parameter-type-list returning T”.

This means h.size has the same type as ::MyClass::size and is evaluated as such, regardless of the fact that h is constexpr or not.

h.size() is then a call to a constexpr function and is a core constant expression according to [expr.const]/4.

Upvotes: 5

Related Questions