为啥这个 constexpr 静态成员函数在调用时不被视为 constexpr?
Posted
技术标签:
【中文标题】为啥这个 constexpr 静态成员函数在调用时不被视为 constexpr?【英文标题】:Why is this constexpr static member function not seen as constexpr when called?为什么这个 constexpr 静态成员函数在调用时不被视为 constexpr? 【发布时间】:2016-05-12 17:56:32 【问题描述】:为什么这个 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:错误:在常量表达式中调用了“静态 constexpr int Item_id::static_n_items()” static 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_
数据有效,为什么这样的规则(对我来说没有意义)?
【问题讨论】:
gcc6.1和clang3.8也不喜欢。 请注意,将bah
的声明和初始化移出struct
并执行static constexpr int bah = Item_id::static_n_items();
会在G++ 5.3 上编译
@GillBates:谢谢,我发现了同样的问题(见修订后的问题)。这很奇怪。
wg21.link/cwg1626
【参考方案1】:
从内存中,成员函数体仅在类被完全定义后才被评估。
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 个不同的,看似无关的子句:)
【讨论】:
我认为你可能是对的,谢谢!但我一直认为重写(在类之后使用函数定义)只是一个概念工具。我不记得在标准中看到它。那么,您知道标准的哪一部分对此做了规定吗? @Cheersandhth.-Alf 半心半意的回应添加到答案中。在漫长的一天结束时,我没有胃口去拖拉所有相关条款的标准……我相信你知道这种感觉! 没有要求在其第一个声明中声明变量constexpr
。【参考方案2】:
[class.mem]/2
在类成员规范中,类在函数体、默认参数、异常规范和默认成员初始化器(包括嵌套类)。否则它在自己的类member-specification中被认为是不完整的。
在类的static
数据成员的初始化程序中,该类不完整。初始化器只能看到它之前的成员声明,它可以看到的任何成员函数都被认为是已声明但未定义。对已声明但未定义的函数的调用不能是常量表达式。
【讨论】:
感谢您的挖掘! [basic.scope.class]/1 用稍微不同的语言说了几乎相同的东西,但我还没有找到支持我的主张的参考资料,即“它可以看到的任何成员函数都被视为已声明但未定义”。以上是关于为啥这个 constexpr 静态成员函数在调用时不被视为 constexpr?的主要内容,如果未能解决你的问题,请参考以下文章
c#里为啥有的使用时函数需要new一个对象而有的不用?为啥不直接调用就好?