20

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?

11
  • 5
    Why not MyClass::size()? Commented Feb 12, 2019 at 10:27
  • 1
    @helloworld922: See a similar bug report on GCC. constexpr member function calls in a constexpr constructor are ignored if the object is defined locally Commented Feb 13, 2019 at 7:47
  • 2
    You could make an argument that, since the left-hand-side of a binary . expression still needs to be evaluated as an expression before it gets resolved (like, I could do (MyClass{} + MyClass{}).size() if MyClass had a operator+(). The fact that the h expression has no side-effects is something that could get collapsed fairly late during the compilation process, so it's not entirely unreasonable to require it to be constexpr. Commented Feb 13, 2019 at 21:01
  • 2
    @Frank If that were the case, I don't understand why when I use h.size() in the context of a template function it fails to compile, but in the context of a non-templated function it compiles just fine. Commented Feb 13, 2019 at 21:36
  • 2
    There’s some uncertainty among implementors/experts as to whether a call to a static member function via a non-constexpr object is a constant expression. You’re not supposed to be able to use such a variable in a constant expression, but the static member function of course doesn’t really use it. Commented Feb 14, 2019 at 4:44

1 Answer 1

5
+50

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.

Sign up to request clarification or add additional context in comments.

2 Comments

Funny enough, MyClass h; std::array<int, h.size()> arr; is rejected by gcc trunk, but std::array<int, MyClass{}.size()> arr; is accepted: godbolt.org/z/GtStLT
FYI, in gcc-7.3, this is the fact that std::array has non-static data members which triggers the bug. Look: godbolt.org/z/Cz38hS

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.