2

I was trying to create a clever class containing a font style. Before this consisted of 3 enums with bit-wise compatible values (each set of values did not had overlapping bits with the other enums) so you could do FontStyle::LEFT | FontStyle::TOP

But clang warned me about combining unrelated enums and yes I've seen possible bugs here: FontStyle::LEFT | FontStyle::RIGHT did set both bits. So I reworked the class using a helper class for the former enums and templates to match correct values. But now I'm getting linker errors for clang on Debug builds about undefined reference to my static constexpr members.

Looking at Undefined reference error for static constexpr member suggests, that the value is ODR-used, but I'm not using any references.

When does a static constexpr class member need an out-of-class definition? this then pointed me to the implicit copy constructor of my helper class which is the issue.

Is there any chance I can avoid the out-of-class definitions in C++14 (C++17 already allows to omit them) and Debug builds (Ctors are optimized away in Release and hence no undefined references)?

Related code:

#include <array>
#include <cstdint>

namespace detail {
template<unsigned T_index>
struct FontStylePart
{
    constexpr FontStylePart(uint8_t val) : value(val) {}
    uint8_t value;
};
} // namespace detail

class FontStyle
{
    static constexpr unsigned AlignH = 0;
    static constexpr unsigned AlignV = 1;

public:
    constexpr FontStyle() = default;

    template<unsigned T_index>
    constexpr FontStyle(detail::FontStylePart<T_index> style) : FontStyle()
    {
        value[T_index] = style.value;
    }

    /// Horizontal align
    static constexpr detail::FontStylePart<AlignH> LEFT = 0;
    static constexpr detail::FontStylePart<AlignH> RIGHT = 1;
    static constexpr detail::FontStylePart<AlignH> CENTER = 2;

    /// Vertical align
    static constexpr detail::FontStylePart<AlignV> TOP = 0;
    static constexpr detail::FontStylePart<AlignV> BOTTOM = 1;
    static constexpr detail::FontStylePart<AlignV> VCENTER = 2;

private:

    std::array<uint8_t, 3> value = {{0, 0, 0}};
};

int main() {
  FontStyle style = FontStyle::CENTER;
  return 0;
}
Flamefire
  • 5,313
  • 3
  • 35
  • 70
  • I do not see any subclass in the code, so it is not clear where exactly ODR use is happening. Can you isolate the problem in MCVE? – SergeyA Mar 25 '19 at 15:33
  • Sorry, mistyped at one place. I meant the helper class not subclass. The MCVE is posted above with link to wandbox which shows the linker error – Flamefire Mar 25 '19 at 15:40
  • I think that this code should work (should not odr-use `FontStyle::CENTER`). Indeed it builds and runs when I change the compiler to clang 8.0.0 – Brian Bi Mar 25 '19 at 18:37
  • @Brian Unfortunately no. Did you set the C++ standard to C++14? In C++14 this does not work for the aforementioned reasons while in C++17 and up it does. – Flamefire Mar 26 '19 at 07:55

1 Answers1

0

The line

FontStyle style = FontStyle::CENTER;

is a ODR use of FontStyle::CENTER.

I tried using

constexpr FontStyle style = FontStyle::CENTER;

but I ran into problems in the constructor. The following works although it's not clear to me whether that is acceptable for your needs.

int main() {
   constexpr auto v = FontStyle::CENTER;
   FontStyle style = v;
   return 0;
}

This transfers the onus of ODR use to constexpr auto v.

R Sahu
  • 204,454
  • 14
  • 159
  • 270