2

I am sorry for my newbie question, but I do not know much about C++. Can anybody answer why I get the error "error: a call to a constructor cannot appear in a constant-expression" when compiling following code;

class EliminationWeight
{
 public:
    typedef double Type;
    static const Type MAX_VALUE = __DBL_MAX__;
    static const Type MIN_VALUE = -__DBL_MAX__;
};

I use Ubuntu 12.04 and gcc that comes with it. It is not my code and I know that this code probably it works OK 100% (perhaps in older version of gcc or other compiler). Is there a quick way to fix it?

Thanks in advance for any answers, this is actually my first time asking something at SO.

Alexandros
  • 2,160
  • 4
  • 27
  • 52

3 Answers3

6

Call to a constructor cannot appear in a constant-expression is a GCC error message which doesn't really make sense to me here. Clang, for instance, accepts your code with some warnings:

test.cpp:31:23: warning: in-class initializer for static data member of type
      'const Type' (aka 'const double') is a GNU extension [-Wgnu]
    static const Type MAX_VALUE = __DBL_MAX__;
                      ^           ~~~~~~~~~~~

Anyway, initializing double in a class body is non-standard. You should do initialization separately:

class EliminationWeight
{
 public:
    typedef double Type;
    static const Type MAX_VALUE;
    static const Type MIN_VALUE;
};

and then in exactly one source file (not a header file):

const EliminationWeight::Type EliminationWeight::MAX_VALUE = __DBL_MAX__;
const EliminationWeight::Type EliminationWeight::MIN_VALUE = -__DBL_MAX__;

In general, you can only initialize static member variables having integral types in class body, although this has been extended in C++ 0x11. See also Initializing const member within class declaration in C++

Community
  • 1
  • 1
vitaut
  • 49,672
  • 25
  • 199
  • 336
  • 4
    ...and that warning is telling you the code is non-standard and works only due to an extension. The problem is that only integral types can be initialized in that way. It's not a "bug" in any way, shape, or form. – Ed S. Jul 03 '12 at 22:50
  • That's true, I am just saying that the error message is wrong. Will update the answer. – vitaut Jul 03 '12 at 22:51
  • -1 removed, though I would describe the problem in a more general way, for instance; "only integral types (which includes enumerated values) may be initialized in that manner." – Ed S. Jul 03 '12 at 22:54
  • Now I get the error: ‘const Type EliminationWeight::MIN_VALUE’ previously defined here – Alexandros Jul 03 '12 at 23:03
  • @AlexandrosE.: Make sure you don't have MIN_VALUE defined twice. Also the initialization part should normally go to the source file. – vitaut Jul 03 '12 at 23:05
  • Thanks for your efforts. Now it compiles fine. Thanks – Alexandros Jul 03 '12 at 23:11
2

I just stumbled across this exact same problem. The code we are discussing is part of the Contraction Hierarchies implementation by KIT.

This is the only compilation error given when trying to compile the code with gcc 4.8.

The fix suggested by @vitaut is what is needed to make this work. However, note that there are already the following lines lingering in main.cpp:

// doesn't look nice, but required by the compiler (gcc 4)
...
const EliminationWeight::Type EliminationWeight::MAX_VALUE;
const EliminationWeight::Type EliminationWeight::MIN_VALUE;

If you decide to create an EliminationWeight.cpp file to go along with your EliminationWeight.h and include it in the Makefile, these lines are the reason why you are seeing a different error than mentioned above:

main.cpp:86:31: error: uninitialized const ‘EliminationWeight::MAX_VALUE’ [-fpermissive]
 const EliminationWeight::Type EliminationWeight::MAX_VALUE;
                               ^
main.cpp:87:31: error: uninitialized const ‘EliminationWeight::MIN_VALUE’ [-fpermissive]
 const EliminationWeight::Type EliminationWeight::MIN_VALUE;
                               ^

The solution is to either remove these lines in main.cpp or use them for the actual initialization. I have gone with the latter and the lines now look like this:

const EliminationWeight::Type EliminationWeight::MAX_VALUE = std::numeric_limits< EliminationWeight::Type >::max();
const EliminationWeight::Type EliminationWeight::MIN_VALUE = -std::numeric_limits< EliminationWeight::Type >::max();

Note that I have used the std::numeric_limits template for the type defined by the EliminationWeight::Type typedef. This means we only need to change that typedef to use a different type.

However, using these in main.cpp mandates that we include the header for the numeric_limits template. It also worked for me without including the header but that is probably because it is included via some other included file. For the purpose of clean code we should include it anyway.

#include <limits>

Also note that C++11 provides a new function lowest for the numeric_limits template meaning you could substitue the last line with the following:

const EliminationWeight::Type EliminationWeight::MIN_VALUE = std::numeric_limits< EliminationWeight::Type >::lowest();

However, the C++ reference on lowest specifies the return value for floating point types as

implementation-dependent; generally, the negative of max()

so I am not sure whether you gain much by using this function. It does give you cleaner looking code but it seems the return value is somewhat unspecified.

Chris
  • 6,914
  • 5
  • 54
  • 80
0

I had this problem when I was going to declare and instantiate a static const object of class Time inside a class Alg. It worked when I declare the member variable inside the class and instantiate it out side, like this:

Class Alg {

    public: 
    .
    .

    static const Time genTime;
    .
    .
}

const Alg::genTime = Time(0,0,1,0);
venerik
  • 5,766
  • 2
  • 33
  • 43
Alaa
  • 1
  • 1