Reputation: 274
I am attempting to write a linked list of pointer-to-member-functions using constexpr. Mostly for fun but it may have a useful application.
struct Foo;
using MethodPtr = void (Foo::*)();
struct Node
{
constexpr Node(MethodPtr method, const Node* next)
: Method(method)
, Next(next)
{}
constexpr Node Push(MethodPtr method)
{
return Node(method, this);
}
MethodPtr Method;
const Node* Next;
};
struct Foo
{
constexpr static Node GetMethods()
{
return Node{&Foo::Method1, nullptr}
.Push(&Foo::Method2)
.Push(&Foo::Method3);
}
void Method1() {}
void Method2() {}
void Method3() {}
};
int main(void)
{
constexpr Node node = Foo::GetMethods();
}
The above code gives me the following error in main on the call to GetMethods():
const Node{MethodPtr{Foo::Method3, 0}, ((const Node*)(& Node{MethodPtr{Foo::Method2, 0}, ((const Node*)(& Node{MethodPtr{Foo::Method1, 0}, 0u}))}))}' is not a constant expression
Will someone please explain why this is not a constant expression? Or is there an alternate/correct way to achieve the goal of building a list of PTMFs at compile time?
EDIT: I am using the C++ compiler from avr-gcc 4.9.2. I will try this code on another compiler.
Upvotes: 3
Views: 3567
Reputation: 2430
There are bugs in this area with gcc. See this link
I've checking std::is_literal_type< Node >() in Visual Studio 2013 and 2015 and I get different answers. Clearly, regardless of the standard, this is still a dark corner of the language...
You have actually taken the address of a temporary and placed it into the list. So, even if you could do this in a const Expr, the Node * points to an invalid object. (You only have one Node object that survives the expression, but you have pointers to two more.)
However, you'll still have problems with a later version because your structure is not a LiteralType.
Upvotes: 0
Reputation: 275385
You are returning a pointer to a temparary variable that goes out of scope.
Pointers to temporary objects are clearly not compile time constant values. Dangling pointers doubly so.
Upvotes: 0
Reputation: 137320
You are storing addresses of non-static-storage-duration temporaries, which is not allowed in constant expressions. The current version of this rule is in [expr.const]/5 (emphasis mine):
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 ([expr.add]), the address of a function, or a null pointer value.
(C++11 contains similar rules (via the definition of address constant expression), but the constant expression rules were changed by multiple DRs before it was replaced by C++14 generalized constexpr
, and I'm not really feeling like doing standard archaeology today.)
In fact, since every temporary Node
created in GetMethods()
except for the Node
that got returned is destroyed at the ;
, the Node
returned would contain a dangling pointer.
Upvotes: 7
Reputation: 11791
You want to get the pointers to Foo
's methods, but Foo
is a class, and you could call this in a derived class (where the methods are different).
Upvotes: 0