Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

static constexpr undefined reference error on clang

The following code compiles fine on Visual Studio 2019 and on gcc 10.2 (and other gcc versions) with -std=c++11 but fails on clang (versions 9, 10 and 11).

#include <map>
#include <string>

struct repo {
    static constexpr const char *x = "sth";
};

int main() {

    // 1) This compiles
    std::map<std::string, int> m1 = { {repo::x, 3} };
    
    // 2) This compiles
    std::map<std::string, std::string> m2 = { std::make_pair(repo::x, "") };
    
    // 3) This does not compile on clang
    std::map<std::string, std::string> m3 = { {repo::x, ""} };

    return 0;
}

The error from clang is:

... undefined reference to `repo::x'
clang-11: error: linker command failed with exit code 1 (use -v to see invocation)
Compiler returned: 1

There are similar questions on SO on this, i.e. Undefined reference error, static constexpr data member, but none that explain to me why this code does not compile on clang. Is there an issue with the code in 3) above?

like image 463
Francis Avatar asked Mar 02 '23 18:03

Francis


1 Answers

C++17 introduces the rule that static constexpr member variables are implicitly inline (P0386). Inline variables didn't exist before C++17.

This means that in earlier C++ standards, a compiler may need a static constexpr member variable to be defined. If its address is taken, for example.

In C++ standards earlier than C++17, you can ensure that your code is well-formed, by separately defining the static variable.

struct repo {
    static constexpr const char *x = "sth";
};

constexpr const char *repo::x;

Edit:

It should be noted that with optimizations off, none of the examples successfully link.

It is an artifact of the optimizer that sometimes, a reference to a value can be flattened to the value itself, in which case the linker won't look for the missing symbol.

like image 176
Drew Dormann Avatar answered Mar 18 '23 05:03

Drew Dormann