我正在尝试使用constexpr编写指向成员函数的指针的链接列表。主要是为了好玩,但它可能有一个有用的应用程序。
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();
}
上面的代码在调用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
有人请解释为什么这不是一个常数表达式?或者是否有一种替代/正确的方法来实现在编译时构建PTMF列表的目标?
编辑:我正在使用avr-gcc 4.9.2中的C ++编译器。我将在另一个编译器上尝试此代码。答案 0 :(得分:6)
您正在存储非静态存储持续时间临时值的地址,这在常量表达式中是不允许的。此规则的当前版本位于[expr.const]/5(强调我的):
常量表达式是glvalue核心常量表达式 其值是指一个允许的结果的实体 常量表达式(如下定义)或prvalue核心常量 表达式,其值是一个对象,对于该对象及其对象 子对象:
引用类型的每个非静态数据成员是指一个实体,它是常量表达式的允许结果,
如果对象或子对象是指针类型,则它包含具有静态存储持续时间的对象的地址,过去的地址 这样一个对象([expr.add])的结尾,一个函数的地址,或者 空指针值。
(C ++ 11包含类似的规则(通过地址常量表达式的定义),但是常量表达式规则在被C ++ 14广义化{{0}替换之前被多个DR更改。 1}},我今天不太喜欢做标准考古学。)
事实上,由于constexpr
中创建的每个临时Node
除了返回的GetMethods()
之外的Node
在;
被销毁,Node
返回包含一个悬垂的指针。
答案 1 :(得分:0)
您希望获得指向Foo
方法的指针,但Foo
是一个类,您可以在派生类(方法不同)中调用它。 / p>
答案 2 :(得分:0)
这个区域有gcc的bug。 See this link
我正在检查std :: is_literal_type< Visual Studio 2013和2015中的节点>(),我得到了不同的答案。显然,无论标准如何,这仍然是语言的一个黑暗角落......
您实际上已获取临时地址并将其放入列表中。因此,即使您可以在const Expr中执行此操作,Node *也会指向无效对象。 (您只有一个在表达式中存活的Node对象,但是您还有两个指针。)
但是,您仍然会遇到更高版本的问题,因为您的结构不是LiteralType。
答案 3 :(得分:0)
您正在返回指向超出范围的临时变量的指针。
指向临时对象的指针显然不是编译时间常量值。悬空指针加倍。