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



这是我的代码,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; }

使用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)

g++ -std=c++11 a.cpp不发出错误。

我的问题是,

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

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

更新:该问题被标记为另一个问题的重复,但它不是。我知道我可以在类外显式地添加定义,让它通过clang。这个问题是关于为什么g++&叮当声。

您似乎认为,如果一个编译器是对的,那么另一个编译器一定是错的。程序要么包含错误(然后接受它的编译器是错误的(,要么没有(然后拒绝它的编译器就是错误的(。这反过来又依赖于一个隐含的假设,即所讨论的错误,即缺少ODR使用实体的定义,是一个可诊断的错误。不幸的是,事实并非如此。该标准明确规定:

[basic.def.odr/10]每个程序都应包含每个非内联函数或变量的一个定义,该函数或变量是odr在该程序中使用的,而不是丢弃的语句无需诊断

尽管该标准的这一条款存在问题且不受欢迎,但它确实存在。由于缺少定义,您的程序有未定义的行为,并且不需要实现来诊断它。因此,两个编译器在任何优化级别上都是技术正确的。

在带有强制拷贝省略的C++17中,程序不再包含任何ODR。使用有问题的变量constexpr静态数据成员是隐式内联的,不需要单独的卸载(感谢Oliv(。

回答我自己的问题。

我对odr的使用有一些模糊的理解。

  • 对于文字类型Foo::bar1,它没有使用odr,所以它很好
  • 对于结构体Foo::bar2:当返回函数体内部的结构体时,它将调用其复制构造函数,该构造函数引用Foo::bar2。所以Foo::bar2是odr使用的,它的定义必须存在于程序中的某个位置,否则会导致链接错误

但是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行,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没有。

相关内容

  • 没有找到相关文章

最新更新