类定义静态constexpr结构的未定义引用,g ++ vs clang

xdo*_*dot 5 c++ one-definition-rule c++11

这是我的代码,a.cp​​p

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; }
Run Code Online (Sandbox Code Playgroud)

用clang编译, 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)
Run Code Online (Sandbox Code Playgroud)

g++ -std=c++11 a.cpp 没有错误.

我的问题是,

  1. 谁对上述代码是正确的?clang还是g ++?
  2. 为什么bar2错误而bar1在clang中是正确的?

编译器版本:g ++ 5.4.0和clang 7.0.0

更新:问题被标记为另一个问题的副本,但事实并非如此.我知道我可以在类之外添加显式定义来为clang传递它.这个问题是为什么g ++和clang之间的区别.

xdo*_*dot 1

回答我自己的问题。

我对odr-use有一些模糊的理解。

  • 对于文字类型 Foo::bar1,它不是 odr 使用的,所以没问题。
  • 对于 struct Foo::bar2: 当在函数体内返回一个结构体时,它将调用其复制构造函数,该构造函数引用Foo::bar2. odr-used也是如此Foo::bar2,它的定义必须存在于程序中的某个地方,否则会导致链接错误。

但为什么g++不抱怨呢?我猜这与编译器优化有关。

验证我的猜测:

  1. 复制省略

    添加-fno-elide-构造函数,g++ -fno-elide-constructors -std=c++11 a.cpp

    /tmp/ccg1z4V9.o:在函数foo2()': a.cpp:(.text+0x27): undefined reference toFoo::bar2'中

    所以,是的,复制省略会影响这一点。但g++ -O1还是能通过。

  2. 内联函数

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

    /tmp/ccH8dguG.o:在函数foo2()': a.cpp:(.text+0x4f): undefined reference toFoo::bar2'中

结论是复制省略和函数内联都会影响其行为。g++ 和 clang 之间的区别是因为 g++ 默认启用复制省略,但 clang 没有。