tags:

views:

392

answers:

5

Is this the proper way to use a static const variable? In my top level class (Shape)

#ifndef SHAPE_H
#define SHAPE_H

class Shape
{
public:

    static const double pi;
private:
    double originX;
    double originY;
};

const double Shape::pi = 3.14159265;

#endif

And then later in a class that extends Shape, I use Shape::pi. I get a linker error. I moved the const double Shape::pi = 3.14... to the Shape.cpp file and my program then compiles. Why does that happen? thanks.

+10  A: 

Because const double Shape::pi = 3.14159265; is the definition of Shape::pi and C++ only allows a single definition of a symbol (called the one-definition-rule which you may see in it's acronym form ODR). When the definition is in the header file, each translation unit gets it's own definition which breaks that rule.

By moving it into the source file, you get only a single definition.

R Samuel Klatchko
+3  A: 

It happens because you can't define Shape::pi more then once. Its defined once when you include Shape.h in Shape.cpp, and then again every other time you use Shape.h in another cpp file. When you go to link you program together the linker will barf because of multiple definitions.

Joe
+1  A: 

The line const double Shape::pi = 3.14159265; should be in your Shape.cpp file. The header file is for declaring the variables. You can only define a variable once, therefore it must be done in the .cpp. The header file says how to use these variables and functions, the cpp file says what to do.

zipcodeman
+1  A: 

Static floating-point data members must be defined and initialized in a source file. The one-definition rule forbids a definition outside the class {} block in the header, and only integral data members are allowed to be initialized inside the class {} block.

This is also unfortunate because, being an algebraic value, having the immediate value on hand could be nice for optimization, rather than loading from a global variable. (The difference is likely to be inconsequential, though.)

There is a solution, though!

class Shape
{
public:
    static double pi()
        { return 3.14159265; }

private:
    double originX;
    double originY;
};

Inline function definitions, including static ones, are allowed inside the class{} block.

Also, I recommend using M_PI from <math.h>, which you should also get from <cmath>.

Potatoswatter
additionally: templates have different linkage, so you could use a template to make the definition visible
Justin
@Justin: I don't think `template` gets you anything here except extra complication.
Potatoswatter
@Potatoswatter: a template may complicate things, but it does potentially buy you an exception to the one definition rule for "static data member of a class template" (among other ODR exceptions). See 3.2/5 and 14.5.1.3 of the C++ Standard.
Michael Burr
@Michael: But identifying that template data member in usage requires a template argument list, which is no less tedious (and a bit less clear) than a function call.
Potatoswatter
A: 

For primitive data types (like int, double but not char[]) you may also define the constant within the class definition within the header file, e.g.:

class Shape
{
public:
    static const double pi = 3.14159265;

private:
    double originX;
    double originY;
};

This will allow better compiler optimisation.

Edit: As Dennis pointed out below this is only allowed for integral types and not for double or float data types (however some compilers will allow it).

Oliver
You can only do this for integral and enum types. Floating point types are not allowed.
Dennis Zickefoose
@Dennis: Oops! You are right! I was fooled by the compiler (gcc and icc allowed static const double to be initialized within the class)...
Oliver