有人能告诉我为什么这不是一个持续的表达?

时间:2016-02-22 01:04:40

标签: c++ c++11 constexpr

我正在尝试使用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 ++编译器。我将在另一个编译器上尝试此代码。

4 个答案:

答案 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)

您正在返回指向超出范围的临时变量的指针。

指向临时对象的指针显然不是编译时间常量值。悬空指针加倍。