static ints, compilation units and the ternary ope

2019-08-02 21:38发布

问题:

// 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;

It is going to build ok. Now take out the method() implementation and place it in a cpp file:

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

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

Why does mr. linker say

undefined reference to SomeCls::PERIOD_ALARM_NORMAL' undefined reference toSomeCls::PERIOD_ALARM_THRESH'

?

Thanks

EDIT: It seems to me that that inside .h, the ternary operator takes static const ints it as rvalues but ... outside the decalrative .h, it regards them as lvalue and needs definition. This is what I have managed to understand from the answers below. Kudos to Bada compiler (some eabi linux thinggie)

回答1:

This is a GCC limitation, but it's completely standard comforming. Technically a static const int is still an lvalue. You've provided the value inline so compiler will almost always use it as an rvalue. There is one exception. The abstract instructions emitted by the compiler for ternary operators queries the address of lvalues. Hence the error you're seeing.

You can work around this by using enum instead. Or if you're using a new version of GCC constexpr was added to standard to fix this exact problem (named and typed rvalues).

Alternatively you can provide the linker with a definition for the constants. E.g. in your classes cpp file add the a line like

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

As a side note: I was a staunch proponent of static const for class scope constants. Then I found out that MSVC doesn't allow for static const float with the value inline. So the only values you can portably put in a static const are integers, in which case enums provide all the same features plus the guarantee that they'll never silently convert to an lvalue.



回答2:

If the compiler can't see all the static class constants' values, then you have to provide definitions for them so that they'll actually be stored somewhere. Add the following to your cpp file:

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


回答3:

If, for whatever reason, you compiler simply refuses to link the code (like GCC 4.4.5 does), here's a simple fix: Replace the static const ints with an 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();
}

This links cleanly with GCC 4.4.5, which wouldn't link the former version, even though both are technically the same.

Note that you cannot, amongst other things, take the address of PERIOD_ALARM_NORMAL and PERIOD_ALARM_TRESH anymore, because both names are just aliases for their respective values.