3

Here's the minimum code:

#include <iostream>
#include <complex>

using namespace std;

class Test {
    static const double dt = 0.1;
public:
    void func();
};

void Test::func() {
    cout << dt << endl; // OK!
    cout << dt*complex<double>(1.0, 1.0) << endl; // Undefined reference
}

int main() {
    Test a;
    a.func();
}

The noted line gives a undefined reference to `Test::dt'. I could make a temporary variable every time I want to multiply a complex number with dt, but that is inconvenient as I am multiply many static const members with complex numbers in my code.

My guess is that when multiplying dt with a complex number, it needs, for some reason, the address of dt (i.e. &dt, which seems weird.

Any ideas why this error happens and how to make it work more elegantly than doing a double temp = dt; before every time I want to multiply it with a complex number?

eimrek
  • 215
  • 2
  • 11
  • This code should not compile. Remove `static`. – Hans Passant Jul 07 '16 at 10:23
  • @HansPassant Yes, it doesn't compile, it gives undefined reference. I need the static const variables though. – eimrek Jul 07 '16 at 10:26
  • No, it doesn't compile because of the `static` keyword: `error: ‘constexpr’ needed for in-class initialization of static data member ‘const double Test::dt’ of non-integral type` I can reproduce the undefined reference error after this compilation error gets fixed. This does indeed look like a compiler bug to me. – Sam Varshavchik Jul 07 '16 at 10:27
  • It doesn't **link**, a very different problem from a compile error. Otherwise accurate, it can't link. Do name the compiler you use so anybody that runs into the same bug knows how to deal with it. – Hans Passant Jul 07 '16 at 10:29
  • @SamVarshavchik Interesting, on gcc 4.8.4 I can compile it well if I comment out the offending line. – eimrek Jul 07 '16 at 10:29
  • 1
    I'm seeing the undefined reference error with gcc 6.1.1. This looks like a gcc compiler bug. – Sam Varshavchik Jul 07 '16 at 10:31

3 Answers3

2

You have declared Test::dt but not defined it somewhere. If you decide do define things outside of class declaration - you should be consistent:

#include <iostream>
#include <complex>

using namespace std;

class Test {
    static const double dt;
    public:
    void func();
};

void Test::func() {
    cout << dt << endl; // OK!
    cout << dt*complex<double>(1.0, 1.0) << endl; // Undefined reference
}

const double Test::dt = 0.1;

int main() {
    Test a;
    a.func();
}
Sergio
  • 8,099
  • 2
  • 26
  • 52
  • This will work, but as I have a lot of constants, it's more convenient and informative to keep the initialization in the header. Currently I still prefer the temporary variable solution. – eimrek Jul 07 '16 at 10:40
  • Maybe simple `enum` would be better in your situation? – Sergio Jul 07 '16 at 10:43
  • Apparently `enum` also gives an error when multiplying with a complex number. – eimrek Jul 07 '16 at 10:51
2

...how to make it work...?

#include <iostream>
#include <complex>

using namespace std;

class Test {
    static const double dt;
public:
    void func();

};

//move initialization outside of class
const double Test::dt = 0.1; 

void Test::func() {
    cout << dt << endl; // OK!
    cout << dt*complex<double>(1.0, 1.0) << endl; // Undefined reference

}

int main() {
    Test a;
    a.func();
}


OR (see this question for explanations)

class Test {
        static const double dt = 0.1;
    public:
        void func();

};
const double Test::dt;


OR (same trick as the one above has, but with c++11's constexpr)

class Test { 
         static constexpr double dt = 0.1;
    public:   
         void func();    

};                      
constexpr double Test::dt;


Any ideas why this error happens...?

From here:

If a static data member of integral or enumeration type is declared const (and not volatile), it can be initialized with a initializer in which every expression is a constant expression, right inside the class definition...

So static data member could be initialized inside of class definition if it's of the type int or enum and declared const, which isn't your case. ( see this answer for more info )

Why it's seems to be working for first line? Well, compiling with clang I got:

warning: in-class initializer for static data member of type 'const double' is a GNU extension

So this float type initialization is extension of gcc compiler, and this extension is probably won't work with function's that expecting reference type argument (just a guess for now).

Also note, that this applies to c++98 only (c++11 has constexpr keyword that is addressing this issue)

Community
  • 1
  • 1
rsht
  • 1,522
  • 12
  • 27
  • Heh, I tried using `static constexpr` instead of `static const` in the code in OP, but the undefined reference still persisted. – eimrek Jul 07 '16 at 11:08
  • about your last edit: if you switch `const`->`constexpr`, then it works and is almost exactly what I asked for in the original post. – eimrek Jul 07 '16 at 11:19
1
#include <iostream>
#include <complex>

using namespace std;

class Test {
    static const double dt;
    public:
    void func();
};

void Test::func() {
    cout << dt << endl; // OK!
    cout << dt*complex<double>(1.0, 1.0) << endl; // Undefined reference
}

const double Test::dt = 0.1;

int main() {
    Test a;
    a.func();
}
John
  • 19
  • 2