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?

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
Dean
  • 6,610
  • 6
  • 40
  • 90
  • 1
    Which version of clang? – Shafik Yaghmour Oct 04 '14 at 19:01
  • Works fine for me: http://coliru.stacked-crooked.com/a/2b319b9351784244 Did you turn on `C++11 flag`? – SwiftMango Oct 04 '14 at 19:11
  • uhm.. http://coliru.stacked-crooked.com/a/bc1887da0ea50cfe – Dean Oct 04 '14 at 19:28
  • Duplicate: [Is a constexpr array necessarily odr-used when subscripted?](http://stackoverflow.com/q/23428684) – dyp Oct 04 '14 at 22:30
  • @dyp according to that post then gcc is wrong, did I get that correct? Is it a bug? – Marco A. Oct 05 '14 at 07:49
  • @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) ;) – dyp Oct 05 '14 at 11:49
  • @dyp 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. – Shafik Yaghmour Feb 11 '15 at 04:03
  • 1
    @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. – AndyG Feb 11 '15 at 05:35
  • Why don't we want to answer questions in comments? Questions without answers are more likely to be automatically removed via [roomba](http://meta.stackexchange.com/a/92006/213111) and comments are second class and even useful comments can be removed at times. – Shafik Yaghmour Feb 11 '15 at 13:44
  • @AndyG You mean the declaration (not definition)? You can safely remove that line and it will still work. – SwiftMango Feb 11 '15 at 22:04
  • @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. – AndyG Feb 12 '15 at 00:56
  • 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. – Shafik Yaghmour Oct 16 '15 at 12:53

1 Answers1

9

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.

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
  • 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. – AndyG Feb 11 '15 at 05:45
  • (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. – AndyG Feb 11 '15 at 05:48
  • @AndyG fair point, I assumed some knowledge, I should have added more detailed. Let me add more details. – Shafik Yaghmour Feb 11 '15 at 13:27
  • 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. – AndyG Feb 11 '15 at 13:43
  • @AndyG no worries, I always welcome constructive comments. You pointed out important details that I did not think to point out. – Shafik Yaghmour Feb 11 '15 at 13:47
  • @T.C. I knew that was in the latest draft but I did not realize it came in via a DR, let me update. – Shafik Yaghmour Oct 16 '15 at 12:46