访问静态constexpr float成员时的未定义引用

时间:2015-02-01 15:20:42

标签: c++ clang constexpr one-definition-rule

此代码有效:

struct Blob {
    static constexpr int a = 10;
};

int main() {
    Blob b;
    auto c = b.a;
}

但如果我将int更改为float,我会收到错误消息:

struct Blob {
    static constexpr float a = 10.0f;
};
  

/tmp/main-272d80.o:在函数main': main.cpp:(.text+0xe): undefined reference to Blob :: a'

为什么我不能以这种方式使用constexpr float

编译器: Ubuntu clang版本3.5.0-4ubuntu2(标签/ RELEASE_350 / final)

在gcc版本4.9.1(Ubuntu 4.9.1-16ubuntu6)上测试并且没有错误。

修改

如果我使用-O1,-O2,-O3或-Os但是会在-O0

时失败而编译

2 个答案:

答案 0 :(得分:6)

C ++ 11读取

  

名称显示为可能评估的表达式的变量是    odr-used 除非它是满足要求的对象   出现在常量表达式(5.19)和左值到右值   转换(4.1)立即应用。

显然,立即应用l-t-r转换,浮点类型的constexpr变量可以按照[expr.const] /(2.7.1)出现在常量表达式中:

  

条件表达式核心常量表达式,除非它   涉及以下之一作为潜在评估的子表达式   [..]

     
      
  • 左值 - 右值转换(4.1),除非适用于      
        
    • 文字类型的glvalue,引用用constexpr 定义的非易失性对象,或引用此类的子对象   对象,或
    •   
  •   

似乎是一个Clang bug。

答案 1 :(得分:3)

有趣的是,如果我们改为使用Blob::aclang就不会抱怨:

auto c = Blob::a;

这对于确定它是否使用过程无关紧要。所以这看起来像clang错误,我可以在clang 3.7上重现,不使用任何优化。我们可以说这是一个odr问题,因为添加一个课外定义可以解决问题( see it live ):

constexpr float Blob::a ;

那么你什么时候需要定义一个静态constexpr类成员?这一点在9.4.2 [class.static.data] 部分中有所说明(强调我的前进):

  

可以在。中声明文字类型的静态数据成员   使用constexpr说明符的类定义;如果是这样,其声明应指定一个支撑或等于初始化器   其中作为赋值表达式的每个initializer子句都是一个常量表达式。 [注意:两者都有   在这些情况下,成员可能出现在常量表达式中。 -end note] 仍然要定义成员   在命名空间范围内,如果程序中使用了odr-used(3.2),则命名空间范围定义不得   包含初始化程序。

如果使用的话,它需要一个定义。它是否经常使用?不它不是。 3.2 [basic.def.odr] 部分中的原始C ++ 11措辞说:

  

表达式可能会被评估,除非它是未评估的操作数(第5条)或子表达式   它们。名称显示为潜在评估表达式的变量除非是一个,否则将被使用   满足出现在常量表达式中的要求的对象(5.19)和左值到左值的对象   转换(4.1)立即应用

a满足两个条件,它是一个常量表达式,并且立即应用左值到右值的转换。 Defect Report 712已经改变了适用于C ++ 11的措辞,因为它是一个缺陷报告,3.2现在说:

  

变量x的名称显示为ex 的潜在评估表达式,除非应用   左值到右值的转换(4.1)到x产生一个不调用任何非平凡值的常量表达式(5.19)   函数和,如果x是一个对象,ex是表达式e的潜在结果集合的元素,其中   要么将左值到右值转换(4.1)应用于e,要么e是丢弃值表达式

匹配的潜在结果是:

  

如果e是id-expression(5.1.1),则该集仅包含e。

它是一个常量表达式,并且应用了左值到右值的转换,因此它不会被使用。