1

The following code snippet can only be linked if optimization level was higher than O0:

    #include <cstdio>
    #include <utility>
    void vf(std::size_t n, ...) {printf("%zu\n", n);}
    template<typename ...ARGS> void vt(ARGS&&... args) {vf( sizeof...(ARGS), std::forward<ARGS>(args)... );}
    struct X {static constexpr int a = 123; X() {vt(a);}};
    int main() {X();}

You can run it here: http://cpp.sh/3dv7p

Configuring C++11/14 with O0 will fail with the following linker error:

/tmp/cc1xC4HI.o: In function `X::X()':
:(.text._ZN1XC2Ev[_ZN1XC5Ev]+0xd): undefined reference to `X::a'
collect2: error: ld returned 1 exit status

Choosing O1, O2 or O3 will link successfully and the program execution returns the expected output.

Solution

It was shown that this problem is not related to the variadic template function. The only question is, if the compiler optimization substituted the static constexpr member during compile time (>O0) or during link time (O0). In the later case, the declaration of X::a also requires some valid definition. This can be done by adding constexpr int X::a; to the above code snippet. The resulting code snippet then will link with any optimization level:

    #include <cstdio>
    #include <utility>
    void vf(std::size_t n, ...) {printf("%zu\n", n);}
    template<typename ...ARGS> void vt(ARGS&&... args) {vf( sizeof...(ARGS), std::forward<ARGS>(args)... );}
    struct X {static constexpr int a = 123; X() {vt(a);}};
    constexpr int X::a;
    int main() {X();}

Please notice that with static constexpr members it is necessary to initialize during declaration, to have complete type, and not during definition.

To let the compiler substitute already during compile-time, also with O0, no reference to X::a should be passed. That's why this snippet will also successfully link with O0, even if it has no definition of X::a:

    #include <cstdio>
    #include <utility>
    void vf(std::size_t n, ...) {printf("%zu\n", n);}
    template<typename ...ARGS> void vt(ARGS... args) {vf( sizeof...(ARGS), std::forward<ARGS>(args)... );}
    struct X {static constexpr int a = 123; X() {vt(a);}};
    int main() {X();}
wiRe
  • 51
  • 4
  • 1
    At higher level optimizations, the compiler realizes your code doesn't do anything, and optimizes it all away. See the dupe for how to properly define a static class member – NathanOliver May 05 '21 at 21:26
  • 1
    Compiles at all optimization levels with C++17. Looks like a C++14 -> C++17 change. live https://godbolt.org/z/nnxdPxc44 try changing just the C++ version. – Richard Critten May 05 '21 at 21:28
  • 1
    @RichardCritten Starting in C++17, static constexpr variables no longer need to have a definition outside of the class. This happened with the introduction of inline variables and the making of constexpr static variables implicitly inline. You can still provide a definition, though it is not required. That was done to not break existing code that had to have those out of line definitions. – NathanOliver May 05 '21 at 21:33
  • @NathanOliver thanks that saved me some research. – Richard Critten May 05 '21 at 21:39
  • @RichardCritten You're welcome. – NathanOliver May 05 '21 at 21:40
  • @NathanOliver: I agree that higher optimization levels will already substitute at compile time, while with O0 this will not be resolved before link time (even it is a ´constexpr´ and I would not expect such a behavior of the compiler). but how do you suggest to properly define the static class member, if you still think it is not done correctly here? – wiRe May 05 '21 at 21:44
  • @wiRe It's explained in the dupe target. You need to add `constexpr int X::a;` outside of the class. – NathanOliver May 05 '21 at 21:49
  • 1
    @wiRe `constexpr int X::a;` seems to fix it - live - https://godbolt.org/z/jqW97f4v8 However not sure when you have multiple translation units. – Richard Critten May 05 '21 at 21:51
  • @NathanOliver Thank you, that fixed my issue! I was not aware that also static constexpr members need some definition, like I am used to do with static members that are not constexpr. It feels not natural to me, since the constexpr declaration requires me to already declare the type inline to have complete type. Also I would expect the use of a constexpr is already substituted at compile time, also with O0. – wiRe May 05 '21 at 22:05

0 Answers0