1

I have this snippet of code I was testing today:

#include <iostream>

struct Literal {
    constexpr operator int() const { return 0; }
};

struct Class {
    constexpr static const Literal field0 = Literal();
};

int main(void) {
    std::cout << "Class::field0: " << Class::field0 << std::endl;
    return 0;
}

It compiles without errors (G++ 6.2.1), but unfortunately I get a link error when generating the executable:

/tmp/ccao6eTy.o: In function `main':
test-constexpr-static-field.cpp:(.text+0xa): undefined reference to `Class::field0'
collect2: error: ld returned 1 exit status
[Finished in 0.3s with exit code 1]

Reading this page I see this explanation:

If a static data member of LiteralType is declared constexpr, it must be initialized with an initializer in which every expression is a constant expression, right inside the class definition (...).

Then I went for the definition of what is a LiteralType, and I saw that:

A literal type is any of the following:

  • possibly cv-qualified class type that has all of the following properties:
    • has a trivial destructor,
    • is either
    • an aggregate type,
    • a type with at least one constexpr (possibly template) constructor that is not a copy or move constructor,
    • a closure type (since C++17)
  • all non-static data members and base classes are of non-volatile literal types.

Is it the case that Literal does not conform to a LiteralType? As I see it, it has a trivial constructor, and even has no internal state to avoid any troublesome issue with aggregate types or non-static fields on a literal type.

But considering it does conform (primarily because the program compiled without errors), why the link-time error? Is that a G++ bug, perhaps?

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
Flávio Lisbôa
  • 651
  • 5
  • 19
  • Where did you provide a definition for `field0`? – Lightness Races in Orbit Dec 30 '16 at 13:45
  • [Links just fine for me](http://coliru.stacked-crooked.com/a/5d693cefda326d00) – Lightness Races in Orbit Dec 30 '16 at 13:46
  • C++11 can be very counter-intuitive sometimes. Indeed, a *definition* is missing. Oh god. – Flávio Lisbôa Dec 30 '16 at 13:49
  • @LightnessRacesinOrbit I see a `--std=c++14` flag in there. I wanted to stay in C++11 if possible (although I think this should be valid in both C++11 and C++14). – Flávio Lisbôa Dec 30 '16 at 13:52
  • http://coliru.stacked-crooked.com/a/328ceae19574bb23 – Lightness Races in Orbit Dec 30 '16 at 16:54
  • @LightnessRacesinOrbit Indeed, it works. I just confirmed that, for this simple case, the problem does not occur because the build is done in a single step. The link error just happens if the units (in this case only one) are translated into object files *before* linking the final binary. – Flávio Lisbôa Dec 30 '16 at 17:28
  • Right, yeah that makes sense – Lightness Races in Orbit Dec 30 '16 at 17:29
  • @LightnessRacesinOrbit Adding the *definition* solves the problem, but adds duplicates if you define it in the header in the very common case of multiple translation units per binary artifact. And, for my initial purpose, this makes the solution unfeasible. As per [this site](http://en.cppreference.com/w/cpp/language/inline), the concept of *inline variable* only exists starting at C++17, which is unfortunate because, as I understand, this makes the definition a required element even for `constexpr static` fields.I can't help but ask why this has begun being supported only in C++17. – Flávio Lisbôa Dec 30 '16 at 17:30
  • Yes, you need to define all statics, even those _initialised_ at the point of declaration. I did suggest that in my first comment! – Lightness Races in Orbit Dec 30 '16 at 17:31
  • @LightnessRacesinOrbit You did point me into the right direction. Your suggestion was the reason why I could find duplicates to my question. Thank you. :) – Flávio Lisbôa Dec 30 '16 at 17:34

1 Answers1

0

Well, apparently I need to add a definition outside the class.

Working example:

#include <iostream>

struct Literal {
    constexpr Literal() = default;
    constexpr operator int() const { return 0; }
};

struct Class {
    constexpr static const Literal field0 = Literal();
};

// This is needed to avoid undefined reference errors
// This is a definition. Very strange.
constexpr const Literal Class::field0;

int main(void) {
    std::cout << "Class::field0: " << Class::field0 << std::endl;
    return 0;
}

Source: https://stackoverflow.com/a/8016853/3962396

Community
  • 1
  • 1
Flávio Lisbôa
  • 651
  • 5
  • 19