Reputation: 10939
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
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 whetherE1.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 returningT
”, thenE1.E2
is an lvalue; the expression designates the static member function. The type ofE1.E2
is the same type as that ofE2
, namely “function of parameter-type-list returningT
”.
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