undefined reference to class static constexpr struct, g++ vs clang

蹲街弑〆低调 提交于 2019-12-23 10:24:08

问题


This is my code, a.cpp

struct int2
{
    int x, y;
};
struct Foo{
    static constexpr int bar1 = 1;
    static constexpr int2 bar2 = {1, 2};
};
int foo1(){
    return Foo::bar1; // this is ok for both clang++ and g++
}
int2 foo2(){
    return Foo::bar2; // undefined reference to `Foo::bar2' in clang++
}
int main(){ std::cout << foo2().x << std::endl; return 0; }

use clang to compile, clang++ -std=c++11 a.cpp

/tmp/a-0dba90.o: In function `foo2()':
a.cpp:(.text+0x18): undefined reference to `Foo::bar2'
clang-7: error: linker command failed with exit code 1 (use -v to see 
invocation)

g++ -std=c++11 a.cpp emits no error.

My question is,

  1. who is right on the above code? clang or g++?
  2. why bar2 is wrong while bar1 is correct in clang?

compiler version: g++ 5.4.0 and clang 7.0.0

UPDATE: The question is marked as a duplicate of another question, but it is not. I know I could add explicitly definition outside class to get it pass for clang. This question is about why all the difference between g++&clang.


回答1:


You seem to assume that if one compiler is right, the other one must be wrong. The program either contains an error (and then the compiler that accepts it is wrong) or it doesn't (and then the compiler that rejects it is wrong). This in turn relies on an implicit assumption that the error in question, namely, missing definition of an ODR-used entity, is a diagnosable error. Unfortunately it isn't. The standard explicitly states that:

[basic.def.odr/10] Every program shall contain exactly one definition of every non-inline function or variable that is odr-used in that program outside of a discarded statement; no diagnostic required.

As problematic and unwanted this provision of the standard is, it's there. Your program has undefined behaviour because of a missing definition, and an implementation is not required to diagnose it. So both compilers are technically correct at any optimisation level.

In C++17 with mandatory copy elision the program no longer contains any ODR-use of the variable in question constexpr static data members are implicitly inline, no separate deinition needed (thanks Oliv).




回答2:


Answer my own question.

I have some vague understanding about odr-use.

  • For literal type Foo::bar1, it is not odr-used, so it is fine.
  • For struct Foo::bar2: when return a struct inside a function body, it will invoke its copy constructor, which take a reference to Foo::bar2. So Foo::bar2 is odr-used, its definition must exist somewhere in the program otherwise it will result a link error.

But why g++ does not complain? I guess it is related to compiler optimization.

Verify my guess:

  1. copy elision

    add -fno-elide-constructors, g++ -fno-elide-constructors -std=c++11 a.cpp

    /tmp/ccg1z4V9.o: In function foo2()': a.cpp:(.text+0x27): undefined reference toFoo::bar2'

    So, yes, copy elision will affect this. But g++ -O1 still get passed.

  2. function inline

    add -fno-line, g++ -O1 -fno-elide-constructors -fno-inline -std=c++11 a.cpp

    /tmp/ccH8dguG.o: In function foo2()': a.cpp:(.text+0x4f): undefined reference toFoo::bar2'

Conclusion is both copy elision & function inline will affect its behavior. The different between g++ & clang is because g++ enabled copy elision by default but clang does not.



来源:https://stackoverflow.com/questions/53407371/undefined-reference-to-class-static-constexpr-struct-g-vs-clang

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!