skip to Main Content
#include <stddef.h>
#include <array>

struct S {
    static constexpr std::array<int,5> sca = {1,2,3,4,5};

    static constexpr int foo(size_t i) {
        return sca[i];
    }
};

int main(int argc, char **argv) {
    S s;
    return s.foo(4);
}

Compiling this gives me a linker error and an undefined reference:

$ g++ --std=c++14 -O0 -o test1 test1.cpp 
/usr/bin/ld: /tmp/ccfZJHBY.o: warning: relocation against `_ZN1S3scaE' in read-only section `.text._ZN1S3fooEm[_ZN1S3fooEm]'
/usr/bin/ld: /tmp/ccfZJHBY.o: in function `S::foo(unsigned long)':
test1.cpp:(.text._ZN1S3fooEm[_ZN1S3fooEm]+0x1a): undefined reference to `S::sca'
/usr/bin/ld: warning: creating DT_TEXTREL in a PIE
collect2: error: ld returned 1 exit status

I checked my g++ version it turned out to be 11.3:

$ g++ --version
g++ (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0
...

The strangest thing is: this code compiles fine in CompilerExplorer with x86-64 gcc 11.3: https://godbolt.org/z/rjG31z9hY

So I thought that maybe it is a compiler version issue, but compiling with g++-12 yielded the same result:

$ /usr/bin/g++-12 --std=c++14 -O0 -o test1 test1.cpp
/usr/bin/ld: /tmp/ccH1PFkh.o: warning: relocation against `_ZN1S3scaE' in read-only section `.text._ZN1S3fooEm[_ZN1S3fooEm]'
/usr/bin/ld: /tmp/ccH1PFkh.o: in function `S::foo(unsigned long)':
test1.cpp:(.text._ZN1S3fooEm[_ZN1S3fooEm]+0x1a): undefined reference to `S::sca'
/usr/bin/ld: warning: creating DT_TEXTREL in a PIE
collect2: error: ld returned 1 exit status

$ /usr/bin/g++-12 --version
g++-12 (Ubuntu 12.1.0-2ubuntu1~22.04) 12.1.0
...

I found out that it works with --std=c++17 and with -O1:

$ g++ --std=c++14 -O1 -o test1 test1.cpp 
$ ./test1 
$ echo $?
5

$ g++ --std=c++17 -O0 -o test1 test1.cpp 
$ ./test1 
$ echo $?
5

I have also found out that binding the result of foo to a variable on the stack somehow fixes the issue:

int main(int argc, char **argv) {
    S s;
    constexpr int x = s.foo(4);
    return x;
}

And allows this code to be built and executed without changing compilation flags:

$ g++ --std=c++14 -O0 -o test2 test2.cpp 
$ ./test2 
$ echo $?
5

Why does it behave like that?

2

Answers


  1. Chosen as BEST ANSWER

    I found a part of the answer in this answer.

    But why it works in CompilerExplorer is still a mystery to me as well as why binding it to a variable on stack fixes the issue.

    Edit 1: CompilerExplorer

    As 273K wrote in his comment, I forgot to check the compiler output option "Link to binary".


  2. Regardless of which of your variations we look at sca[i] in foo odr-uses sca. (You don’t even need to call foo at all for this to be the case.) As a result a definition for sca must be available. However, if none is available the program is ill-formed, no diagnostic required (IFNDR), meaning that the compiler may issue a diagnostic for the problem, but doesn’t have to.

    Practically speaking, it is just a matter of whether or not the compiler optimized the access to sca away by inferring what value you would read from it instead of actually calling foo. Since foo is also implicitly inline the compiler doesn’t have to emit a definition for it (but some compilers do), so whether or not there will be a reference to sca in the object file for the linker to resolve will depend on optimization and compiler/linker specifics.

    Before C++17, static constexpr std::array<int,5> sca = {1,2,3,4,5}; is not a definition of sca and so your program is IFNDR. All compilers you tried are behaving correctly. There doesn’t need to be any warning or error for this. A definition std::array<int,5> S::sca; must be added after the class definition to make the program well-formed.

    Since C++17 static constexpr std::array<int,5> sca = {1,2,3,4,5}; is a definition (because the constexpr makes it implicitly inline) and the program is well formed in either variation. Again, the compilers are behaving correctly. Before C++17 there were no inline static data members at all, so constexpr couldn’t imply it.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search