tags:

views:

309

answers:

6

In C++, is there any reason to not access static member variables through a class instance? I know Java frowns on this and was wondering if it matters in C++. Example:

class Foo {
  static const int ZERO = 0;
  static const int ONE = 1;
  ...
};


void bar(const Foo& inst) {
   // is this ok?
   int val1 = inst.ZERO;
   // or should I prefer:
   int val2 = Foo::ZERO
   ...
};

I have a bonus second question. If I declare a static double, I have to define it somewhere and that definition has to repeat the type. Why does the type have to be repeated? For example:

In a header:
  class Foo {
    static const double d;
  };
In a source file:
  const double Foo::d = 42;

Why do I have to repeat the "const double" part in my cpp file?

+5  A: 

I would prefer Foo::ZERO over inst.ZERO because it more clearly tells what is going on. However, in a method of class Foo, I would just use ZERO.

As for the bonus question, const just is part of the complete type.

Pukku
I understand that const is part of the type. I'm wondering why I have to repeat the type at all. I'd like to just say: Foo::d = 42;There's only one Foo::d declared in the header file. Does this introduce the possibility of some ambiguity that I'm not seeing here? Could there be member function named d? I know there's a good reason for repeating the type, I just don't know what that is.
criddell
Maybe that is just the rules for the language?
Juan
I tried to explain this in a comment but ran out of room (sigh). I answer it below.
quark
+3  A: 

Given that you are declaring them as static and make them class constants, I would use Foo::Zero to communicate the intent to the casual and not so casual reader of your code.

I would also replace the all uppercase names of the constants, ie turn Foo::ZERO into Foo::Zero. Normal convention for preprocessor macros is to name them in all uppercase and using a similar naming scheme for your C++ constants is asking for trouble because the preprocessor might just blat over your C++ constants and you end up with very interesting error messages.

Timo Geusch
Wow- good point on the naming convention. I've actually been bitten by this more than one time (please, everybody-- stop #define'ing PI!!!)
criddell
+1  A: 

I would use Foo::ZERO, but that is just me. Especially if you derive from Foo, which gets confusing.

For your second question, you need to create memory for the double value, and that happens in an implementation unit.

I think the only type you don't need to create memory for is a const integral type. You can then put the value in the header file. But since it doesn't have an address, you wouldn't be able to pass it by reference to a function, unless you put a definition in a .cpp file. (This seems to be the way it works with gcc).

Juan
+1  A: 

It doesn't matter which form you use in your example. They both mean the same thing. I'd prefer to use the class way in general because you may not always have an instance handy to use the dot operator on.

It is nice to have both options if you consider someone writing a template function. They may have coded the function with the dot operator. Your class with the static class member could still be used to instantiate the template because that syntax is supported.

As for your bonus question, that's just the way the language is. You always have to declare the full type. You should probably ask that in a separate question.

Brian Neal
+1  A: 

personally i'd use an anonymous enum. End result is exactly the same though :)

As for your questions. I'd definitely prefer the Foo::Zero because its obvious from looking at it what you are accessing. inst.Zero requires you to work out what type inst is before hand.

You have to repeat the datatype because thats how C++ works. In the same way if you wrote the following in a header file.

extern int foo;

You will still need to mention the

int foo

in a CPP file. As pukku mentioned you are declaring a variable of type "const int". Thus the "const int" must be repeated in the definition of the variable.

Goz
anonymous enums force a particular storage size (int). If you want the constants to be of a different type, then you can't use anonymous enums.
Chris Cleeland
+3  A: 

For the first question, aside from the matter of style (it makes it obvious it's a class variable and has no associated object), Fred Larsen, in comments to the question, makes reference to previous question. Read Adam Rosenthal's answer for very good reason why you want to be careful with this. (I'd up-vote Fred if he'd posted it as answer, but I can't so credit where it's due. I did up-vote Adam.)

As to your second question:

Why do I have to repeat the "const double" part in my cpp file?

You have to repeat the type primarily as an implementation detail: it's how the C++ compiler parses a declaration. This isn't strictly ideal for local variables either, and C++1x (formerly C++0x) makes use of the auto keyword to avoid needing to be repetitive for regular function variables.

So this:

vector<string> v;
vector<string>::iterator it = v.begin();

can become this:

vector<string> v;
auto it = v.begin();

There's no clear reason why this couldn't work with static as well, so in your case thos:

const double Foo::d = 42;

could well become this.

static Foo::d = 42;

The key is to have some way of identifying this as a declaration.

Note I say no clear reason: C++'s grammar is a living legend: it is extremely hard to cover all of its edge cases. I don't think the above is ambiguous but it might be. If it isn't they could add that to the language. Tell them about it ... for C++2x :/.

quark