【问题标题】:static ints, compilation units and the ternary operator静态整数、编译单元和三元运算符
【发布时间】:2011-12-19 23:31:48
【问题描述】:

// 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 toSomeCls::PERIOD_ALARM_THRESH'的未定义引用

?

谢谢

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

【问题讨论】:

  • 为什么当你做what时链接器会这样说?您要准确链接什么?
  • @Scarlet:我认为你在这里误解了一些东西。 OP不想测试d == b,他想分配d,测试b
  • 请不要对变量名使用全大写。它们传统上仅用于预处理器宏(除了阅读起来很累)。
  • @Xeo 我是盲人 :) 删除评论以免造成误解,谢谢。
  • FWIW,此代码在 Clang 3.1 上运行良好,但在 GCC 4.4.5 上运行良好。你用的是哪个编译器?

标签: c++ compilation linker undefined


【解决方案1】:

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

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

【讨论】:

  • +1,我不知道“如果看不到他们的价值观”部分
  • @SethCarnegie:嗯,它应该是相反的方式......“在好的情况下你可能不需要定义,因为编译器已经知道这些值并且可以在不需要实际变量的情况下将它们折叠到代码中”......我认为让函数定义脱机是编译器坚持定义的充分理由。
  • 只有当他在任何地方获取这些变量的地址时,才需要翻译单元中的实际定义。他没有,所以他给出的例子应该编译得很好。
  • @Xeo:我认为这是因为该函数是离线定义的,因此如果您想从其他地方调用该函数,它需要该常量作为变量。用inline 试试吧,也许你会在没有定义的情况下逃脱。
  • @Xeo : 如果他'ODR-uses'那些变量,则需要翻译单元中的定义,这并不严格限于获取它们的地址。
【解决方案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 中的唯一值是整数,在这种情况下,enums 提供所有相同的功能,并保证它们永远不会默默地转换为 lvalue

【讨论】:

  • 这很简单。我只是希望您对左值/右值的解释是正确的,并且我正在选择最准确的答案:P。编辑:似乎它不准确。这并没有解释它在第一种情况下是如何工作的 - .h 中的所有内容
  • 非整数类型的内联初始化仅允许在 C++11 中用于 static constexpr。这与 MSVC 无关...
  • 请注意,它只是符合之前 C++03 标准的标准。在 C++11 中,ODR 措辞发生了变化:§3.2 [basic.def.odr] p2 "[...] 名称显示为可能求值表达式的变量是 odr-used,除非它是一个满足出现在常量表达式中的要求 (5.19) [...]"。我非常想说static const int 确实 满足上述要求。
  • @KerrekSB,我一直认为static const 内联初始化标准只需要内置类型而不是整数类型。所以看起来 GCC 允许 float 是一个扩展。标准是否说static constexprs 中允许浮动?无论哪种方式,优先顺序(用于编译器可移植性)static constexprenum,然后是static const integral
【解决方案3】:

如果出于某种原因,您的编译器只是拒绝链接代码(就像 GCC 4.4.5 那样),这里有一个简单的解决方法:将 static const ints 替换为 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 的地址,因为这两个名称只是它们各自值的别名。

【讨论】:

  • 但是常量有一个独特的、未命名的类型。
  • @curiousguy:开心吗?好像它会在 OP 显示的示例中有所不同。
  • 在这种情况下可能没有什么不同,但 enumstatic const int 之间的差异会影响像 C++ 这样的语言中的代码,这可能会令人惊讶。我认为值得一提。
猜你喜欢
  • 2017-05-12
  • 2015-09-21
  • 1970-01-01
  • 2015-07-13
  • 1970-01-01
  • 2017-05-23
  • 1970-01-01
  • 2012-11-09
  • 1970-01-01
相关资源
最近更新 更多