tags:

views:

862

answers:

6

Is it better to use static const vars than #define preprocessor? Or maybe it depends on the context?

What are advantages/disadvantages for each method?

+3  A: 

Using a static const is like using any other const variables in your code. This means you can trace wherever the information comes from, as opposed to a #define that will simply be replaced in the code in the pre-compilation process.

You might want to take a look at the C++ FAQ Lite for this question: http://www.parashift.com/c++-faq-lite/newbie.html#faq-29.7

JP
A: 

Please see here: static const vs define

usually a const declaration (notice it doesn't need to be static) is the way to go

ennuikiller
+4  A: 

Personally, I loathe the preprocessor, so I'd always go with const.

The main advantage to a #define is that it requires no memory to store in your program, as it is really just replacing some text with a literal value. It also has the advantage that it has no type, so it can be used for any integer value without generating warnings.

Advantages of "const"s are that they can be scoped, and they can be used in situations where a pointer to an object needs to be passed.

I don't know exactly what you are getting at with the "static" part though. If you are declaring globally, I'd put it in an anonomous namespace instead of using static. For example

namespace {
   unsigned const seconds_per_minute = 60;
};

int main (int argc; char *argv[]) {
...
}
T.E.D.
That's what I want to know. In my company, the strategy is to put all string constant as #define in a separate file. I was thinking it can be better to use const vars instead. Thanks
Patrice Bernassola
*String* constants specifically are one of those that might benefit from being `#define`d, at least if they can be used as "building blocks" for bigger string constants. See my reply for an example.
AndreyT
A case I had not considered. Thanks for your time.
Patrice Bernassola
The `#define` advantage of not using any memory is inaccurate. The "60" at the example has to be stored somewhere, regardless if it's `static const` or `#define`. In fact, I've seen compilers where using #define caused massive (read-only) memory consumption, and static const used no un-needed memory.
Gilad Naor
+3  A: 

If this is a C++ question and it mentions #define as an alternative, then it is about "global" (i.e. file-scope) constants, not about class members. When it comes to such constants in C++ static const is redundant. In C++ const have internal linkage by default and there's no point in declaring them static. So it is really about const vs. #define.

And, finally, in C++ const is preferable. At least because such constants are typed and scoped. There are simply no reasons to prefer #define over const, aside from few exceptions.

String constants, BTW, are one example of such an exception. With #defined string constants one can use compile-time concatenation feature of C/C++ compilers, as in

#define OUT_NAME "output"
#define LOG_EXT ".log"
#define TEXT_EXT ".txt"

const char *const log_file_name = OUT_NAME LOG_EXT;
const char *const text_file_name = OUT_NAME TEXT_EXT;

P.S. Again, just in case, when someone mentions static const as an alternative to #define, it usually means that they are talking about C, not about C++. I wonder whether this question is tagged properly...

AndreyT
+1  A: 
  • A static const is typed (it has a type) and can be checked by the compiler for validity, redefinition etc.
  • a #define can be redifined undefined whatever.

Usually you should prefer static consts. It has no disadvantage. The prprocessor should mainly be used for conditional compilation (and sometimes for really dirty trics maybe).

RED SOFT ADAIR
+2  A: 

(Originally posted in response to another question that was closed for duplicating this one - reproducing my answer here incase the other closed question is purged - do let me know if this isn't an appropriate procedure)

Pros and cons to everything, depending on usage:

  • enums
    • only possible for integer values
    • properly scoped / identifier clash issues handled nicely
    • strongly typed, but to a big-enough signed-or-unsigned int size over which you have no control (in C++03)
    • can't take the address
    • stronger usage restraints (e.g. incrementing - template <typename T> void f(T t) { cout << ++t; } won't compile)
    • each constant's type taken from the enclosing enum, so template <typename T> void f(T) get a distinct instantiation when passed the same numeric value from different enums, all of which are distinct from any actual f(int) instantiation.
    • even with typeof, can't expect numeric_limits to provide useful insight
    • the enum's typename may appear in various places in RTTI, compiler messages etc. - possibly useful, possibly obfuscation
  • consts
    • properly scoped / identifier clash issues handled nicely
    • strong, single, user-specified type
      • you might try to "type" a #define ala #define S std::string("abc"), but the constant avoids repeated construction of distinct temporaries at each point of use
    • One Definition Rule complications
    • can take address, create const references to them etc.
  • defines
    • "global" scope / more prone to conflicting usages, which can produce hard-to-resolve compilation issues and unexpected run-time results rather than sane error messages; mitigating this requires:
      • long, obscure and/or centrally coordinated identifiers, and access to them can't benefit from implicitly matching used/current/Koenig-looked-up namespace, namespace aliases etc.
      • use of all uppercase characters is generally required and reserved for preprocessor defines (an important guideline for enterprise scale preprocessor usage to remain manageable, and which 3rd party libraries can be expected to follow), observation of which implies migration of existing consts or enums to defines involves a change in capitalisation (and hence affects client code). (Personally, I capitalise the first letter of enums but not consts, so I'd be hit here anyway - maybe time to rethink that.)
    • more compile-time operations possible: string literal concatenation, stringification (taking size thereof)
      • downside is that given #define X "x" and some client usage ala "pre" X "post", you're in trouble if you want or need to make X a runtime-changeable variable rather than a constant, whereas that transition is easier from a const char* or const std::string given they already force the user to incorporate concatenation operations.
    • can't use sizeof directly on a defined numeric constant
    • untyped (GCC doesn't warn if compared to unsigned)
    • some compiler/linker/debugger chains may not present the identifier, so you'll be reduced to looking at "magic numbers" (strings, whatever...)
    • can't take the address
    • the substituted value need not be legal (or discrete) in the context where the #define is created, as it's evaluated at each point of use, so you can reference not-yet-declared objects, depend on "implementation" that needn't be pre-included, create "constants" such as { 1, 2 } that can be used to initialise arrays, or #define MICROSECONDS *1E-6 etc. (definitely not recommending this!)
    • some special things like __FILE__ and __LINE__ can be incorporated into the macro substitution

As a general rule, I use consts and consider them the most professional option for general usage (though the others have a simplicity appealing to this old lazy programmer).

Tony
+1: Appreciate your summarization
Chubsdad