Static constexpr odr-used or not?
Asked Answered
K

1

11

How come that the following works on gcc but doesn't on clang, (see it live):

constexpr int giveMeValue() { return 42; }

struct TryMe {
  static constexpr int arr[1] = {
      giveMeValue()
  };  
};

int main() {
    int val = TryMe::arr[0];
    return val;
}

I get an unresolved external symbol with clang.

Is TryMe::arr[0] an object? If it is, is it odr-used?

Kettledrummer answered 4/10, 2014 at 18:58 Comment(12)
Which version of clang?Tong
Works fine for me: coliru.stacked-crooked.com/a/2b319b9351784244 Did you turn on C++11 flag?Bradytelic
uhm.. coliru.stacked-crooked.com/a/bc1887da0ea50cfeKettledrummer
Duplicate: Is a constexpr array necessarily odr-used when subscripted?Buttonball
@Buttonball according to that post then gcc is wrong, did I get that correct? Is it a bug?Randell
@MarcoA. odr-violations are not required to be diagnosed.. so you could only ask the g++ developers nicely to add a diagnostic that it's an extension (or UB) ;)Buttonball
@Buttonball I don't think it is a duplicate of that question at least, since the main question here is why are the results inconsistent. Although closely related they are not the same.Tong
@texasbruce: Your implementation works because you added a definition constexpr int TryMe::arr[1]; outside struct TryMe which makes it so there is no odr violation. OP's code lacks such a definition.Rutheruthenia
Why don't we want to answer questions in comments? Questions without answers are more likely to be automatically removed via roomba and comments are second class and even useful comments can be removed at times.Tong
@Rutheruthenia You mean the declaration (not definition)? You can safely remove that line and it will still work.Bradytelic
@texasbruce: Hmm, perhaps the compiler is optimizing away the vbl declaration because it's unused. If you try to return val out of main() you will get the error.Rutheruthenia
I updated my answer, due to defect report 1926 your code should be well formed in C++1z and I think C++14 as well.Tong
T
10

TryMe::arr is odr-used but you don't provide a definition (see it live):

constexpr int TryMe::arr[1];

Why is the result inconsistent between gcc and clang? This is because odr violations do not require a disagnostic, from both the C++11 and C++14 draft standard (emphasis mine):

Every program shall contain exactly one definition of every non-inline function or variable that is odr-used in that program; no diagnostic required.

We can see it is odr-used from the draft C++11 standard, section 3.2 which says:

An expression is potentially evaluated unless it is an unevaluated operand (Clause 5) or a subexpression thereof. A variable whose name appears as a potentially-evaluated expression is odr-used unless it is an object that satisfies the requirements for appearing in a constant expression (5.19) and the lvalue-to-rvalue conversion (4.1) is immediately applied.

TryMe::arr is an object and it does satisfy the requirements for appearing in a constant expression but the lvalue-to-rvalue conversion is not immediately applied to TryMe::arr but to TryMe::arr[0].

The updated wording from the draft C++14 standard which applies to C++11 as well since it was applied via a defect report(DR 712):

A variable x whose name appears as a potentially-evaluated expression ex is odr-used unless applying the lvalue-to-rvalue conversion (4.1) to x yields a constant expression (5.19) that does not invoke any non-trivial functions and, if x is an object, ex is an element of the set of potential results of an expression e, where either the lvalue-to-rvalue conversion (4.1) is applied to e, or e is a discarded-value expression

The potential results of the expression TryMe::arr[0] is empty by the criteria in 3.2 paragraph 2 and so it is odr-used.

Note: you need to provide a definition outside of the class as per section 9.4.2 [class.static.data] which says (emphasis mine):

A static data member of literal type can be declared in the class definition with the constexpr specifier; 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. [ Note: In both these cases, the member may appear in constant expressions. —end note ] The member shall still be defined in a namespace scope if it is odr-used (3.2) in the program and the namespace scope definition shall not contain an initializer

Update

T.C. pointed out defect report 1926 which adds the following bullet to 3.2 [basic.def.odr] paragraph 2:

  • If e is a subscripting operation (5.2.1 [expr.sub]) with an array operand, the set contains that operand.

Which means subscripting an array is no longer an odr-use and so the OPs code would be well-formed in C++1z and it seems like C++14 since the defect looks like it is against C++14.

Tong answered 11/2, 2015 at 4:1 Comment(6)
Some more clarification about declaration vs definition. What OP has in his TryMe struct may appear to be a definition, but in fact is a declaration because of the funny rules about when a declaration is also a definition. Here OP has a static variable with an in-class member initializer, which is usually disallowed, but is allowed here because of constexpr (and int). However, OP hasn't actually allocated memory for TryMe::arr because the variable is also static which requires external storage. So, like any static variable, OP still must provide a declaration outside the struct.Rutheruthenia
(Continuing previous comment because I ran out of room). However, it's not a good idea to provide the definition constexpr int TryMe::arr[1]; in the same file that OP defines the TryMe struct because any class that wants to include the definition for struct TryMe will then attempt to allocate storage for TryMe::arr[1], violating the One Definition Rule. So what usually happens instead is that you would put the constexpr int TryMe::arr[1] in a .cpp file of some sort, separate from the header.Rutheruthenia
@Rutheruthenia fair point, I assumed some knowledge, I should have added more detailed. Let me add more details.Tong
I thought your post was great! I just wanted to add some extra detail for those lurkers who happened upon it, so it wasn't a jab at you.Rutheruthenia
@Rutheruthenia no worries, I always welcome constructive comments. You pointed out important details that I did not think to point out.Tong
@T.C. I knew that was in the latest draft but I did not realize it came in via a DR, let me update.Tong

© 2022 - 2024 — McMap. All rights reserved.