静态int,编译单元和三元运算符

时间:2011-12-19 23:31:48

标签: c++ compilation linker undefined

// SomeCls.h

class SomeCls
    {
    static const int PERIOD_ALARM_NORMAL    =   5;          
    static const int PERIOD_ALARM_THRESH    =   1;          

    void method()
    {
        bool b = true;
        const int d = b ? PERIOD_ALARM_THRESH : PERIOD_ALARM_NORMAL;
    }

    } obj;

它将构建好。现在取出method()实现并将其放在cpp文件中:

 //SomeCls.cpp
#include "SomeCls.h"

void SomeCls::method()
    {
        bool b = true;
        const int d = b ? PERIOD_ALARM_THRESH : PERIOD_ALARM_NORMAL;
    }

为什么先生。链接器说

  

SomeCls::PERIOD_ALARM_NORMAL' undefined reference to SomeCls :: PERIOD_ALARM_THRESH'的未定义引用

由于

编辑: 在我看来,在.h内部,三元运算符将静态const作为rvalues,但是......在decalrative .h之外,它将它们视为左值并需要定义。 这是我从下面的答案中设法理解的。感谢Bada编译器(一些eabi linux thinggie)

3 个答案:

答案 0 :(得分:3)

如果编译器无法看到所有静态类常量',那么您必须为它们提供定义,以便它们实际存储在某处。将以下内容添加到cpp文件中:

const int SomeCls::PERIOD_ALARM_NORMAL;
const int SomeCls::PERIOD_ALARM_THRESH;

答案 1 :(得分:2)

这是GCC限制,但它完全符合标准。从技术上讲,static const int仍为lvalue。您已提供内联值,因此编译器几乎总是将其用作rvalue。有一个例外。编译器为三元运算符发出的抽象指令查询lvalues的地址。因此你看到了错误。

您可以使用enum来解决此问题。或者,如果您使用新版本的GCC constexpr已添加到标准中以修复此确切问题(命名和键入的右值)。

或者,您可以为链接器提供常量的定义。例如。在你的类cpp文件中添加一行像

// I wish I had constexpr
const int SomeCls::PERIOD_ALARM_NORMAL;
const int SomeCls::PERIOD_ALARM_THRESH;

作为旁注:对于类范围常量,我是static const的坚定支持者。然后我发现MSVC不允许static const float具有内联值。因此,您可以移植到static const中的唯一值是整数,在这种情况下,enum提供所有相同的功能,并保证它们永远不会以静默方式转换为lvalue

答案 2 :(得分:1)

如果由于某种原因,编译器只是拒绝链接代码(如GCC 4.4.5那样),这里有一个简单的修复:用static const int替换enum

// someclass.h
// include guards, blabla

class SomeClass
{
    enum AlarmPeriod{
      PERIOD_ALARM_NORMAL = 5,
      PERIOD_ALARM_THRESH = 1
    };      

public:
    void method();
};

// someclass.cpp
#include "someclass.h"

void SomeClass::method(){
    bool b = true;
    const int d = b ? PERIOD_ALARM_THRESH : PERIOD_ALARM_NORMAL;
}

// main.cpp
#include "someclass.h"

int main(){
  someclass sc;
  sc.method();
}

这与GCC 4.4.5完全联系,后者不会链接前一版本,即使两者技术上都相同。

请注意,除其他外,您不能再使用PERIOD_ALARM_NORMALPERIOD_ALARM_TRESH的地址,因为这两个名称只是各自值的别名。