简体   繁体   English

constexpr 使用静态函数初始化静态成员

[英]constexpr initializing static member using static function

Requirements要求

I want a constexpr value (ie a compile-time constant) computed from a constexpr function.我想要从constexpr函数计算的constexpr值(即编译时常量)。 And I want both of these scoped to the namespace of a class, ie a static method and a static member of the class.而且我希望这两个范围都在类的命名空间内,即类的静态方法和静态成员。

First attempt第一次尝试

I first wrote this the (to me) obvious way:我首先用(对我来说)显而易见的方式写了这个:

class C1 {
  constexpr static int foo(int x) { return x + 1; }
  constexpr static int bar = foo(sizeof(int));
};

g++-4.5.3 -std=gnu++0x says to that: g++-4.5.3 -std=gnu++0x说:

error: ‘static int C1::foo(int)’ cannot appear in a constant-expression
error: a function call cannot appear in a constant-expression

g++-4.6.3 -std=gnu++0x complains: g++-4.6.3 -std=gnu++0x抱怨:

error: field initializer is not constant

Second attempt第二次尝试

OK, I thought, perhaps I have to move things out of the class body.好吧,我想,也许我必须把东西移出类体。 So I tried the following:所以我尝试了以下方法:

class C2 {
  constexpr static int foo(int x) { return x + 1; }
  constexpr static int bar;
};
constexpr int C2::bar = C2::foo(sizeof(int));

g++-4.5.3 will compile that without complaints. g++-4.5.3将毫无怨言地编译它。 Unfortunately, my other code uses some range-based for loops, so I have to have at least 4.6.不幸的是,我的其他代码使用了一些基于范围的for循环,所以我必须至少有 4.6。 Now that I look closer at the support list , it appears that constexpr would require 4.6 as well.现在我仔细查看了支持列表,似乎constexpr也需要 4.6。 And with g++-4.6.3 I get使用g++-4.6.3我得到

3:24: error: constexpr static data member ‘bar’ must have an initializer
5:19: error: redeclaration ‘C2::bar’ differs in ‘constexpr’
3:24: error: from previous declaration ‘C2::bar’
5:19: error: ‘C2::bar’ declared ‘constexpr’ outside its class
5:19: error: declaration of ‘const int C2::bar’ outside of class is not definition [-fpermissive]

This sounds really strange to me.这对我来说听起来很奇怪。 How do things “differ in constexpr ” here?这里的东西“在constexpr有何不同”? I don't feel like adding -fpermissive as I prefer my other code to be rigurously checked.我不想添加-fpermissive因为我更喜欢严格检查我的其他代码。 Moving the foo implementation outside the class body had no visible effect.foo实现移到类体之外没有明显的效果。

Expected answers预期答案

Can someone explain what is going on here?有人可以解释这里发生了什么吗? How can I achieve what I'm attempting to do?我怎样才能实现我正在尝试做的事情? I'm mainly interested in answers of the following kinds:我主要对以下类型的答案感兴趣:

  • A way to make this work in gcc-4.6一种在 gcc-4.6 中完成这项工作的方法
  • An observation that later gcc versions can deal with one of the versions correctly观察到后来的 gcc 版本可以正确处理其中一个版本
  • A pointer to the spec according to which at least one of my constructs should work, so that I can bug the gcc developers about actually getting it to work一个指向规范的指针,根据该规范,我的至少一个构造应该可以工作,以便我可以让 gcc 开发人员真正让它工作
  • Information that what I want is impossible according to the specs, preferrably with some insigt as to the rationale behind this restriction根据规范,我想要的信息是不可能的,最好是对此限制背后的基本原理有所了解

Other useful answers are welcome as well, but perhaps won't be accepted as easily.其他有用的答案也是受欢迎的,但可能不会那么容易被接受。

The Standard requires (section 9.4.2):该标准要求(第 9.4.2 节):

A static data member of literal type can be declared in the class definition with the constexpr specifier;可以在类定义中使用constexpr说明符声明文字类型的static数据成员; if so, its declaration shall specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression.如果是这样,它的声明应指定一个大括号或等号初始化器,其中每个作为赋值表达式的初始化器子句都是一个常量表达式。

In your "second attempt" and the code in Ilya's answer, the declaration doesn't have a brace-or-equal-initializer .在您的“第二次尝试”和 Ilya 的答案中的代码中,声明没有大括号或相等的初始化程序

Your first code is correct. 你的第一个代码是正确的。 It's unfortunate that gcc 4.6 isn't accepting it, and I don't know anywhere to conveniently try 4.7.x (eg ideone.com is still stuck on gcc 4.5). 不幸的是 gcc 4.6 不接受它,而且我不知道在哪里可以方便地尝试 4.7.x(例如 ideone.com 仍然停留在 gcc 4.5 上)。

This isn't possible, because unfortunately the Standard precludes initializing a static constexpr data member in any context where the class is complete.这是不可能的,因为不幸的是,标准禁止在类完成的任何上下文中初始化静态constexpr数据成员。 The special rule for brace-or-equal-initializers in 9.2p2 only applies to non-static data members, but this one is static. 9.2p2 中大括号或相等初始化器的特殊规则仅适用于非静态数据成员,但这是静态的。

The most likely reason for this is that constexpr variables have to be available as compile-time constant expressions from inside the bodies of member functions, so the variable initializers are completely defined before the function bodies -- which means the function is still incomplete (undefined) in the context of the initializer, and then this rule kicks in, making the expression not be a constant expression:最可能的原因是constexpr变量必须作为编译时常量表达式从成员函数体内部可用,因此变量初始值设定项在函数体之前完全定义——这意味着函数仍然不完整(未定义) ) 在初始值设定项的上下文中,然后此规则生效,使表达式不是常量表达式:

an invocation of an undefined constexpr function or an undefined constexpr constructor outside the definition of a constexpr function or a constexpr constructor;未定义的调用constexpr功能或未定义constexpr a的定义之外构造constexpr函数或constexpr构造;

Consider:考虑:

class C1
{
  constexpr static int foo(int x) { return x + bar; }
  constexpr static int bar = foo(sizeof(int));
};

1) Ilya's example should be invalid code based on the fact that the static constexpr data member bar is initialized out-of-line violating the following statement in the standard: 1) Ilya 的示例应该是无效代码,因为静态 constexpr 数据成员 bar被初始化为违反标准中的以下语句:

9.4.2 [class.static.data] p3: ... A static data member of literal type can be declared in the class definition with the constexpr specifier; 9.4.2 [class.static.data] p3: ... 可以在类定义中使用 constexpr 说明符声明文字类型的静态数据成员; if so, its declaration shall specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression.如果是这样,它的声明应指定一个大括号或等号初始化器,其中每个作为赋值表达式的初始化器子句都是一个常量表达式。

2) The code in MvG's question: 2)MvG问题中的代码:

class C1 {
  constexpr static int foo(int x) { return x + 1; }
  constexpr static int bar = foo(sizeof(int));
};

is valid as far as I see and intuitively one would expect it to work because the static member foo(int) is defined by the time processing of bar starts (assuming top-down processing).就我所见和直觉上来说是有效的,因为静态成员 foo(int)是由bar开始的时间处理定义的(假设自上而下处理)。 Some facts:一些事实:

  • I do agree though that class C1 is not complete at the point of invocation of foo (based on 9.2p2) but completeness or incompleteness of the class C1 says nothing about whether foo is defined as far as the standard is concerned.我确实同意,尽管在调用 foo 时类 C1并不完整(基于 9.2p2),但是类 C1 的完整性或不完整性并没有说明就标准而言是否定义了 foo。
  • I did search the standard for the definedness of member functions but didn't find anything.我确实搜索了成员函数定义的标准,但没有找到任何东西。
  • So the statement mentioned by Ben doesn't apply here if my logic is valid:因此,如果我的逻辑有效,Ben 提到的陈述不适用于这里:

    an invocation of an undefined constexpr function or an undefined constexpr constructor outside the definition of a constexpr function or a constexpr constructor;在 constexpr 函数或 constexpr 构造函数的定义之外调用未定义的 constexpr 函数或未定义的 constexpr 构造函数;

3) The last example given by Ben, simplified: 3)Ben给出的最后一个例子,简化了:

 class C1 { constexpr static int foo() { return bar; } constexpr static int bar = foo(); };

looks invalid but for different reasons and not simply because foo is called in the initializer of bar .看起来无效,但出于不同的原因,而不仅仅是因为foobar的初始值设定项中被调用。 The logic goes as follows:逻辑如下:

  • foo() is called in the initializer of the static constexpr member bar , so it has to be a constant expression (by 9.4.2 p3). foo()静态 constexpr 成员 bar的初始化程序中被调用,因此它必须是一个常量表达式(9.4.2 p3)。
  • since it's an invocation of a constexpr function, the Function invocation substitution (7.1.5 p5) kicks in.因为它是对 constexpr 函数的调用,所以函数调用替换(7.1.5 p5) 开始了。
  • Their are no parameters to the function, so what's left is "implicitly converting the resulting returned expression or braced-init-list to the return type of the function as if by copy-initialization."它们没有函数的参数,所以剩下的是“将结果返回的表达式或花括号初始化列表隐式转换为函数的返回类型,就像通过复制初始化一样。” (7.1.5 p5) (7.1.5 p5)
  • the return expression is just bar , which is a lvalue and the lvalue-to-rvalue conversion is needed.返回表达式只是bar ,它是一个左值,需要进行左值到右值的转换。
  • but by bullet 9 in (5.19 p2) which bar does not satisfy because it is not yet initialized:但通过在子弹9(5.19 P2),该不满足,因为它尚未初始化:

    • an lvalue-to-rvalue conversion (4.1) unless it is applied to:左值到右值的转换 (4.1) 除非它应用于:
      • a glvalue of integral or enumeration type that refers to a non-volatile const object with a preceding initialization, initialized with a constant expression.整数或枚举类型的泛左值,它引用具有前面初始化的非易失性 const 对象,用常量表达式初始化。
  • hence the lvalue-to-rvalue conversion of bar does not yield a constant expression failing the requirement in (9.4.2 p3).因此bar的左值到右值转换不会产生不符合 (9.4.2 p3) 要求的常量表达式。

  • so by bullet 4 in (5.19 p2), the call to foo() is not a constant expression:因此,根据 (5.19 p2) 中的第 4 项,对foo()的调用不是常量表达式:

    an invocation of a constexpr function with arguments that, when substituted by function invocation substitution (7.1.5), do not produce a constant expression调用带有参数的 constexpr 函数,当被函数调用替换 (7.1.5) 替换时,不会产生常量表达式

#include <iostream>

class C1 
{
public:
    constexpr static int foo(constexpr int x)
    { 
        return x + 1;
    }

    static constexpr int bar;
};

constexpr int C1::bar = C1::foo(sizeof(int));

int main()
{
    std::cout << C1::bar << std::endl;
    return 0;
}

Such initialization works well but only on clang这种初始化效果很好,但只适用于 clang

Probably, the problem here is related to the order of declaration/definitions in a class.可能,这里的问题与类中声明/定义的顺序有关。 As you all know, you can use any member even before it is declared/defined in a class.众所周知,您甚至可以在类中声明/定义任何成员之前使用它。

When you define de constexpr value in the class, the compiler does not have the constexpr function available to be used because it is inside the class.当您在类中定义 de constexpr 值时,编译器没有可用的 constexpr 函数,因为它在类内部。

Perhaps, Philip answer, related to this idea, is a good point to understand the question.也许,与此想法相关的Philip回答是理解问题的好点。

Note this code which compiles without problems:请注意此代码编译没有问题:

constexpr int fooext(int x) { return x + 1; }
struct C1 {
  constexpr static int foo(int x) { return x + 1; }
  constexpr static int bar = fooext(5);
};

constexpr static int barext = C1::foo(5);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 使用派生类的静态constexpr数据成员初始化基类的静态constexpr数据成员 - Initializing a static constexpr data member of the base class by using a static constexpr data member of the derived class Constexpr静态成员函数用法 - Constexpr static member function usage 通过调用constexpr函数定义静态constexpr成员 - Defining a static constexpr member by call to constexpr function 使用表达式找不到模板化的静态constexpr成员函数 - static constexpr member function in templated using expression not found std::enable_if 基于使用静态 constexpr 成员函数的表达式 - std::enable_if based on expression using static constexpr member function 静态成员函数不被接受为constexpr参数 - Static member function not accepted as constexpr parameter 使用静态 constexpr 成员无法解析的外部符号 - Unresolved external symbol using static constexpr member 使用引用的 constexpr 静态成员作为模板参数 - Using a constexpr static member of a reference as template argument 为什么在调用此constexpr静态成员函数时不将其视为constexpr? - Why is this constexpr static member function not seen as constexpr when called? 调用并初始化class的成员static function - Calling and initializing static member function of class
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM