5

This is what I've got:

struct Foo
{
   static std::array<double, 4> acgt_default_background_frequencies() { return {0.281774, 0.222020, 0.228876, 0.267330}; }
};

But I'd prefer to not use a function and instead just have a variable, like this:

struct Foo
{
   static constexpr std::array<double, 4> acgt_default_background_frequencies = {0.281774, 0.222020, 0.228876, 0.267330};
};

What I want compiles, but when I try to use Foo::acgt_default_background_frequencies it gives the linker error "undefined reference to `Foo::acgt_default_background_frequencies'".

Is what I am trying to do possible? I think it is clearer to the reader of my header if I have the value inlined as a const than to hide it in the .cpp file and having a constant as opposed to a function also seems clearer. Isn't the point of constexpr to allow stuff like this? If it isn't possible, why not?

JDiMatteo
  • 12,022
  • 5
  • 54
  • 65
  • Your function approach is very inefficient as it copies the entire array each time it is called. You could fix this by making the array static and returning a reference to it. – Neil Kirk Oct 18 '15 at 23:08

1 Answers1

6

What you have in the second example is a declaration of a static data member which has an initializer, but you haven't provided a definition anywhere. If you make odr-use of that member, a definition will be required.

To provide a definition, add the following to your .cpp file

constexpr std::array<double, 4> Foo::acgt_default_background_frequencies;

The declaration in the question works in C++14, but note that in C++11 you need an extra set of curly braces, e.g.

struct Foo
{
  static constexpr std::array<double, AlphabetSize> acgt_default_background_frequencies = {{0.281774, 0.222020, 0.228876, 0.267330}};
};

The relevant standardese from N3337 §9.4.2/3 [class.static.data]

... 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. ... 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.

JDiMatteo
  • 12,022
  • 5
  • 54
  • 65
Praetorian
  • 106,671
  • 19
  • 240
  • 328
  • 1
    How many copies of the array will there be in the final program? – Neil Kirk Oct 18 '15 at 22:54
  • Thanks that works, but I'm still confused why a `static constexpr std::array` needs a definition in the .cpp file while a `static constexpr double` doesn't. – JDiMatteo Oct 18 '15 at 22:57
  • @NeilKirk There will only be one `Foo::acgt_default_background_frequencies`, just like any other static data member. – Praetorian Oct 18 '15 at 22:58
  • 1
    @JDiMatteo That requires one too if you make odr-use of the variable. See [this](http://coliru.stacked-crooked.com/a/058af8f7e733ac1c). If you just print the value of the double, that doesn't constitute odr-use, so [this](http://coliru.stacked-crooked.com/a/8742e162218bf61c) works. – Praetorian Oct 18 '15 at 23:00
  • @Praetorian, great thanks, your comment combined with your link on odr-use makes it perfectly clear. – JDiMatteo Oct 18 '15 at 23:02
  • @JDiMatteo well underneath std::array is likely an array and until C++1z [indexing an array is an odr-use](http://stackoverflow.com/a/28446388/1708801). This seems to effectively appy to C++14 since it was applied via a defect report not sure about C++11 though, it was never clear to me how far back defect reports are suppose to apply. – Shafik Yaghmour Oct 19 '15 at 00:05
  • @NeilKirk The purpose of the requirement for a global variable's definition is to say which translation unit the one copy of that variable lives in. The *odr-use* text formalizes the idea that if the program does anything that actually refers to the storage for the variable, then we must say where that storage is. – M.M Oct 19 '15 at 01:18
  • @ShafikYaghmour Thanks for linking to that, but it still doesn't apply to the OP's example, does it? The DR linked to in your answer is careful to say that the operand has to be an array, but in this case it isn't, you're calling an operator overload. – Praetorian Oct 19 '15 at 19:12
  • @Praetorian wow, I completely forgot that operator[] returns a reference which is clearly an odr-use. Otherwise I think it would be ok. – Shafik Yaghmour Oct 20 '15 at 16:39
  • Could you comment on why `arr[0]` counts as *odr-use* of `arr` , preferably with close reference to [basic.def.odr] ? The issue came up again [here](http://stackoverflow.com/questions/35927878/assign-static-constexpr-class-member-to-runtime-variable/) – M.M Mar 11 '16 at 01:21