【问题标题】:static const in c++ class: undefined referenceC ++类中的静态常量:未定义的引用
【发布时间】:2013-06-06 08:41:14
【问题描述】:

我有一个仅供本地使用的类(即,它的副本只是它定义的 c++ 文件)

class A {
public:
    static const int MY_CONST = 5;
};

void fun( int b ) {
    int j = A::MY_CONST;  // no problem
    int k = std::min<int>( A::MY_CONST, b ); // link error: 
                                            // undefined reference to `A::MY_CONST` 
}

所有代码都位于同一个 c++ 文件中。在windows上使用VS编译时,完全没有问题。
但是,在 Linux 上编译时,我只收到第二条语句的 undefined reference 错误。

有什么建议吗?

【问题讨论】:

  • 不要指定函数模板参数。只需std::min(A::MY_CONST, b),它更干净。
  • @KerrekSB - 一般来说你是对的。我从我的代码中修改了这个例子。 b 不是 int,因此我需要明确指定模板参数。
  • 你是如何构建这个的?我无法重现 gcc 4.7.3 的问题。
  • 编译得很好:ideone.com/2LlrHt
  • "我从我的代码中修改了这个例子。" => 但是您是否至少检查过您的发布的代码确实收到了发布的错误? (我问是因为我无法重现错误。)

标签: c++ linker static-members


【解决方案1】:

std::min&lt;int&gt; 的参数都是const int&amp;(不仅仅是int),即引用int。而且您不能传递对A::MY_CONST 的引用,因为它未定义(仅声明)。

在类外的.cpp 文件中提供定义:

class A {
public:
    static const int MY_CONST = 5; // declaration
};

const int A::MY_CONST; // definition (no value needed)

【讨论】:

  • @djf 确实,也许这就是我无法真正重现该错误的原因。 (但我仍然认为始终提供定义可以避免潜在的问题;如果不需要,编译器无论如何都会将其删除。如果您想要阻止获取其地址,则可以使用旧的enum把戏。)
  • 对于完成static const int 的处理方式与其他数据类型不同,请参阅this answer
  • MY_CONST 赋值为 5 时没有定义吗?您的意思是“声明为 5”与“定义为 5”?
  • 这是最好的解决方案。仅值定义的建议可能不适合 MY_CONST 的值出现在其他成员声明中的某些情况。
  • 这不是很令人满意。常量表达式的 std::min 应该在编译时计算;如果编译器无法弄清楚如何使用静态 const 成员来做到这一点,那么我会说静态 const 成员还没有准备好迎接黄金时段,#define MY_CONST 5 更可取。
【解决方案2】:
// initialize static constants outside the class

class A {
public:
    static const int MY_CONST;
};

const int A::MY_CONST = 5;

void fun( int b ) {
    int j = A::MY_CONST;  // no problem
    int k = std::min<int>( A::MY_CONST, b ); // link error: 
                                            // undefined reference to `A::MY_CONST` 
}

【讨论】:

  • 这解决了问题,因为它提供了一个定义。如果有定义,原始代码也可以工作,就像这里的那个但没有初始化器。
【解决方案3】:

解释这里发生了什么:

您在类中声明了static const 整数,这个“功能”在这里能够将其用作常量表达式,即对于本地数组大小、模板非类型参数等。如果编译器想要使用这个常量表达式,它必须能够在那个翻译单元中看到它的值。

9.5/3

如果非易失性 const 静态数据成员是整型或枚举类型,则其在类中的声明 定义可以指定一个大括号或等式初始化器,其中作为赋值表达式的每个初始化器子句都是一个常量表达式(5.19)。文字类型的静态数据成员可以在 使用 constexpr 说明符进行类定义;如果是这样,它的声明应指定一个大括号或相等初始化器 其中每个作为赋值表达式的初始化子句都是常量表达式。 [注:在两者中 在这些情况下,成员可能出现在常量表达式中。 — 尾注] 成员仍应被定义 如果在程序中使用了 odr-used (3.2) 并且命名空间范围定义 不应在命名空间范围内 包含一个初始化器

odr-used 表示形成对该变量的引用或获取它的地址。

std::min 通过引用获取它的参数,因此它们是 odr-used

解决方案:

定义它!

class A
{
    static const int a = 5;
};

const int A::a; //definition, shall not contain initializer

【讨论】:

    【解决方案4】:

    您也可以将 const 值保存到局部变量中。

    class A {
    public:
        static const int MY_CONST = 5;
    };
    
    void fun( int b ) {
        int j = A::MY_CONST;  // no problem
        int k = std::min<int>( A::MY_CONST, b ); // link error: undefined reference to `A::MY_CONST` 
        int l = std::min<int>( j, b);  // works
    }
    

    【讨论】:

      【解决方案5】:

      我的情况很奇怪

      template<class T> class Strange {
      
      public:
        static const char gapchar='-';
        };
      
      template<class T> void Strange<T> method1 {
            char tmp = gapchar;
      }
      
      template<class T> void Strange<T> method2 {
          char tmp = gapchar;
      }
      

      我包括上面的类,它已经工作了好几年了。

      我添加了另一种方法,本质上是相同的签名,只是读取 gapchar。

      我只遇到了第三种方法的未定义错误,即使我正在使用所有三种方法。

      然后我改变了我初始化静态变量的方式

      不在类定义中初始化:

      static const char gapchar;
      
      template<class T> const char Strange<T>::gapchar='-';
      

      这解决了问题。我不明白为什么旧的方式 在类中初始化 int 或 char 类型(仅允许的两种类型) 定义部分仅对其中一种方法停止工作,而不对其他方法起作用。

      【讨论】:

        【解决方案6】:

        如果您正在使用一些仅包含标头的内容并且希望避免添加 .cpp 文件,您似乎可以这样做:

        class A {
        public:
            static inline const int MY_CONST = 5;
        };
        
        (or `static inline constexpr`)
        
        This requires C++17
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-08-04
          • 1970-01-01
          • 2013-04-11
          • 2013-07-05
          • 1970-01-01
          相关资源
          最近更新 更多