21

I have a class for local use only (i.e., its cope is only the c++ file it is defined in)

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` 
}

All the code reside in the same c++ file. When compiling using VS on windows, there is no problem at all.
However, when compiling on Linux I get the undefined reference error only for the second statement.

Any suggestions?

  • Don't specify function template arguments. Just `std::min(A::MY_CONST, b)`, it's cleaner. – Kerrek SB Jun 06 '13 at 08:46
  • @KerrekSB - in general you are right. I modified this example from my code. There `b` was not `int` and therefore I needed to explicitly specify the template argument. –  Jun 06 '13 at 08:51
  • related answer (not dup) the special status of `static ints`: http://stackoverflow.com/a/1312267/2379182 –  Jun 06 '13 at 08:52
  • possible dup of: http://stackoverflow.com/questions/5391973/undefined-reference-to-static-const-int – billz Jun 06 '13 at 08:52
  • 2
    How did you build this? I cannot reproduce the problem with gcc 4.7.3. – juanchopanza Jun 06 '13 at 08:55
  • 1
    Compiles just fine: http://ideone.com/2LlrHt – djf Jun 06 '13 at 08:56
  • 1
    "I modified this example from my code." => But did you check at least that you _actually get the posted error_ with your _posted code_? (I ask because I can't reproduce the error.) – gx_ Jun 06 '13 at 08:57
  • I reproduced it on gcc4.8 – billz Jun 06 '13 at 08:57
  • @billz strange, I can't reproduce on my gcc 4.8 snapshot. – juanchopanza Jun 06 '13 at 09:12
  • I am using `icpc` - intel C compiler. –  Jun 06 '13 at 09:16
  • Possible duplicate of [Undefined reference to static class member](http://stackoverflow.com/questions/272900/undefined-reference-to-static-class-member) – manlio Jun 23 '16 at 08:08

6 Answers6

26

std::min<int>'s arguments are both const int&(not just int), i.e. references to int. And you can't pass a reference to A::MY_CONST because it is not defined (only declared).

Provide a definition in the .cpp file, outside the class:

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

const int A::MY_CONST; // definition (no value needed)
gx_
  • 4,690
  • 24
  • 31
  • @djf Indeed, maybe that's why I couldn't actually reproduce the error. (But I still think it avoids potential problems to always provide a definition; if not needed the compiler will strip it out anyway. If you _want_ to prevent taking its address you can just use the old `enum` trick.) – gx_ Jun 06 '13 at 09:03
  • For completion `static const int` are treated differently than other data types, see [this answer](http://stackoverflow.com/a/1312267/2379182). –  Jun 06 '13 at 09:05
  • Isn't MY_CONST defined when you assign it 5? You mean it is "declared to be 5" versus "defined to be 5"? – David Doria Mar 22 '16 at 17:09
  • this is the best solution. the suggestion of definition with value only may not fit some situations that value of *MY_CONST* appears in declaration of other members. – ZFY Apr 06 '18 at 14:39
  • This isn't very satisfying. std::min of constant expressions should be evaluated at compile time; if the compiler can't figure out how to do that using static const members, then I'd say static const members aren't ready for prime time and `#define MY_CONST 5` is preferable. – Don Hatch Feb 02 '19 at 03:46
  • @DonHatch `enum { MY_CONST = 5 };` would be preferable to polluting global "namespace" with macros – M.M May 20 '19 at 12:42
6
// 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` 
}
pserg
  • 61
  • 1
  • This solves the problem because it provides a definition. The original code would also work if there was a definition, just like the one here but without the initializer. – Pete Becker Jun 06 '13 at 17:57
4

To explain what's happening here:

You declared static const integer inside class, this "feature" is here to be able to use it as constant expression,i.e. for local array size, template non-type parameters, etc.. If compiler wants to use this constant expression it must be able to see it's value in that translation unit.

9.5/3

If a non-volatile const static data member is of integral or enumeration type, its declaration in the class definition can specify a brace-or-equal-initializer in which every initializer-clause that is an assignment expression is a constant expression (5.19). A static data member of literal type can be declared in the class definition with the constexpr specifier; if so, its declaration shall specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression. [ Note: In both these cases, the member may appear in constant expressions. — end note ] The member shall still be defined in a namespace scope if it is odr-used (3.2) in the program and the namespace scope definition shall not contain an initializer.

odr-used means to form reference to that variable or take it's address.

std::min takes it's parameters by reference, so they are odr-used.

Solution:

Define it!

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

const int A::a; //definition, shall not contain initializer
PcAF
  • 1,975
  • 12
  • 20
2

You can also save the const value to a local variable.

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
}
Jodo
  • 4,515
  • 6
  • 38
  • 50
0

I am having a very strange situation

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

I include the above class, it has been working for several years.

I added another method, essentially the same signature and just reading the gapchar.

I got undefined error only for the third method, even I am using all three methods.

Then I changed the way I initialize the static variable by

not initializing in the class definition:

static const char gapchar;

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

This solved the problem. I could not figure out why the old way of initializing int or char type (the only two types allowed) inside the class definition section stop working for only one of the methods but not others.

Kemin Zhou
  • 6,264
  • 2
  • 48
  • 56
0

If you are using some header-only thing and want to avoid having to add a .cpp file, it seems like you can do this:

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

(or `static inline constexpr`)

This requires C++17
Timmmm
  • 88,195
  • 71
  • 364
  • 509