What should I do with a static, constexpr, in-class initialized data element?

This is probably a slightly unusual question, since it requires a more complete explanation of the short answer given to another question , and some aspects of the C ++ 11 Standard associated with it.

For ease of reference, I will summarize the question here. OP defines a class:

struct Account { static constexpr int period = 30; void foo(const int &) { } void bar() { foo(period); } //no error? }; 

and wonders why he has no errors in his use of the initialized static class member in the class (the book mentions that this is illegal). Johannes Schaub replies that:

  • This violates the One Definition rule;
  • No diagnostics required.

As far as I rely on the source and reliability of this answer, I sincerely do not like it, because I personally find it too mysterious, so I tried to develop a more meaningful answer myself with partial success. § 9.4.2 / 4 appears relevant:

"The program must have exactly one definition of a static data member, odr-used (3.2) does not require diagnostics " [My emphasis ]

Which brings me a little closer to the point. And this is how § 3.2 / 2 defines the odr variable used :

"A variable whose name is displayed as a potentially evaluable expression is used if it is not an object that meets the requirements for displaying in constant expression (5.19) and the lvalue-to-rvalue transformation (4.1) is immediately applied." [My characters]

In the OP question, the period variable explicitly satisfies the requirements for appearing in a constant expression, which is the constexpr variable. Therefore, the reason must be found in the second condition: " and the lvalue-to-rvalue (4.1) transformation is immediately applied ."

Here I am having trouble interpreting the Standard. What does this second condition mean? What are the situations that it covers? Does this mean that the static variable constexpr not used not odr (and therefore can be initialized in the class) if it is returned from the function?

More generally: What can you do with the constexpr static variable constexpr that you can initialize it inside the class?

+8
c ++ c ++ 11 static-members constexpr one-definition-rule
Jan 27 '13 at 13:45
source share
2 answers

Does this mean that the static constant constexpr is not used by odr (and therefore can be initialized in the class) if it is returned from a function?

Yes.

Essentially, if you consider it as a value, not an object, then it is not used by odr. Note that if you put in a value, the code will work the same way - this is when it is considered as an rvalue. But there are some scenarios in which this would not be.

There are only a few scenarios in which the lvalue-to-rvalue conversion is not performed on primitives, and this link binding is &obj and possibly a couple others, but this is very small. Remember that if the compiler gives you const int& , referring to period , you should be able to accept its address, and in addition, this address should be the same for each TU. This means that in C ++ the terrifying TU system must have one explicit definition.

If it is not used by odr, the compiler can make a copy in each TU, or replace the value, or whatever, and you cannot notice the difference.

+3
Jan 27 '13 at 14:10
source share

You missed part of the room. The above class definition is perfectly valid if you also define Account::period somewhere (but without providing an initializer). See last post 9.4.2 / 3:

An element still needs to be defined in the namespace area if it is used as odr (3.2) in the program, and the namespace area definition should not contain an initializer.

All of this discussion applies only to constexpr static data elements, and not to namespace-scope static variables. When the static data members are constexpr (and not just const), you must initialize them in the class definition. So your question should really be: What can you do with a constexpr static data element so that you don’t have to give a definition for it in the namespace area?

From the previous one, the answer is that the member should not be used as odr. And you have already found the relevant parts of the odr-used definition. Thus, you can use an element in a context where it is not potentially evaluated, like an invaluable operand (e.g. sizeof or decltype ) or its subexpression. And you can use it in an expression that immediately applies the lvalue-to-rvalue transform.

So now we get to . What use of a variable causes an immediate conversion of lvalue to rvalue?

Part of this answer is given in §5 / 8:

Whenever a glvalue expression appears as the operand of an operator that expects a prvalue for that operand, lvalue-to-rvalue (4.1), the array-to-pointer standard (4.2), or the standard conversion pointer function (4.3) are used to transform the expression in prvalue.

For arithmetic types, which are mainly applied to all operators that apply standard arithmetic conversions. Thus, you can use the element in various arithmetic and logical operations without requiring a definition.

I cannot list all the things that you can or cannot do here, because the requirements that something is [g] lvalue or [p] rvalue are spread by standard. Rule of thumb: if only the value for the variable is used, the conversion of lvalue to rvalue is applied; if a variable is used as an object , it is used as an lvalue.

You cannot use it in contexts where the lvalue value is explicitly required, for example, as an argument to an address operator or mutating operators). Direct linking of lvalue links (without conversion) is such a context.

A few more examples (without detailed standard analysis):

You can pass it to functions if the function parameter is not a reference to which you can directly bind a variable.

To return from a function: if the implicit conversion to the type of the returned object includes a user-defined conversion function, the rules apply to go to the function. Otherwise, you can return it if the function does not return an lvalue (reference) value that refers directly to the variable.

The key rule for variables using odr, the "One Definition Rule" is in 3.2 / 3:

Each program must contain exactly one definition of each non-line function or variable that is used in this program odr; No diagnostic required.

The component “without the need for diagnostics” means that programs that violate this rule cause undefined behavior, which can vary from failure to compile, compile and crash in amazing compilation methods and actions, as if everything is in order. And your compiler should not warn you about the problem.

The reason, as others have already pointed out, is that many of these violations will only be detected by the linker. But optimization may have removed object references, so there is no reason for the connection to fail, otherwise the connection can sometimes occur only at run time or be determined to select an arbitrary instance from several name definitions.

+3
Jan 27 '13 at 15:34
source share



All Articles