为什么这个 constexpr 静态成员函数在调用时不被视为 constexpr?

问题描述 投票:0回答:2

为什么这个由

constexpr
注释标识的
static
//! Nah
成员函数在调用时不显示为
constexpr

struct Item_id
{
    enum Enum
    {
        size, position, attributes, window_rect, max_window_size, _
    };

    static constexpr int n_items_ = _;                          // OK
    constexpr auto member_n_items() const -> int { return _; }  // OK
    static constexpr auto static_n_items() -> int { return _; } // OK
    static constexpr int so_far = n_items_;                     // OK
    #ifndef OUT_OF_CLASS
        static constexpr int bah = static_n_items();            //! Nah.
    #endif
};

constexpr auto n_ids() -> int { return Item_id().member_n_items(); }    // OK

auto main() -> int
{
    #ifdef OUT_OF_CLASS
        static constexpr int bah = Item_id::static_n_items();   // OK
    #endif
}

MinGW g++ 5.1 报告

constexpr.cpp:12:46:错误:在常量表达式中调用“static constexpr int Item_id::static_n_items()”
     静态 constexpr int bah = static_n_items(); //!不。

Visual C++ 2015 报告

constexpr.cpp(12):错误 C2131:表达式未计算为常量
constexpr.cpp(12):注意:失败是由于调用未定义的函数或未声明“constexpr”的函数引起的
constexpr.cpp(12):注意:请参阅 'Item_id::static_n_items' 的用法

我的文本编辑器坚持要求调用中的名称与函数定义中的名称相同。

它似乎与不完整的类有关,因为定义了

OUT_OF_CLASS
它可以很好地编译。

但是为什么

n_items_
数据有效,为什么这样的规则(对我来说没有意义)?

c++ constexpr static-members member-functions
2个回答
16
投票

根据记忆,只有在类完全定义后才会计算成员函数体。

static constexpr int bah = static_n_items(); 

构成类定义的一部分,但它指的是尚未定义的(静态)成员函数。

解决方案:

将常量表达式推迟到基类并从中派生。

例如:

struct Item_id_base
{
    enum Enum
    {
        size, position, attributes, window_rect, max_window_size, _
    };

    static constexpr int n_items_ = _;                          // OK
    constexpr auto member_n_items() const -> int { return _; }  // OK
    static constexpr auto static_n_items() -> int { return _; } // OK
    static constexpr int so_far = n_items_;                     // OK
};

struct Item_id : Item_id_base
{
    #ifndef OUT_OF_CLASS
        static constexpr int bah = static_n_items();            // now OK
    #endif
};

constexpr auto n_ids() -> int { return Item_id().member_n_items(); }    // OK

auto main() -> int
{
    #ifdef OUT_OF_CLASS
        static constexpr int bah = Item_id::static_n_items();   // OK
    #endif
}

您认为为什么标准不允许这样做?

因为这是非法的:

struct Item_id
{   
    // ... etc.

    #ifndef OUT_OF_CLASS
        static constexpr int bah;// = static_n_items();            //! Nah.
    #endif
};

constexpr int Item_id::bah = static_n_items();

并且 constexpr 必须有 constexpr 定义。我们唯一可以定义它的地方是在它的声明期间......

...因此通过推论,它不能引用任何尚未定义其主体的函数。

我不知道在哪里可以找到所有这些标准。可能有 5 个不同的、看似无关的子句:)


6
投票

[类.mem]/2

在类成员规范中,该类在函数体、默认参数、异常规范和默认成员初始值设定项(包括嵌套类中的此类内容)中被视为完整。否则,它在自己的类中被视为不完整成员规范

在类的

static
数据成员的初始化程序中,该类是不完整的。初始化程序只能看到它前面的成员的声明,并且它可以看到的任何成员函数都被视为已声明但未定义。对已声明但未定义的函数的调用不能是常量表达式。

© www.soinside.com 2019 - 2024. All rights reserved.