static const vs #define

前端 未结 11 884
醉话见心
醉话见心 2020-11-22 13:01

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

What are advantages/disadvantages for

相关标签:
11条回答
  • 2020-11-22 13:24

    Please see here: static const vs define

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

    0 讨论(0)
  • 2020-11-22 13:31

    Pros and cons between #defines, consts and (what you have forgot) enums, depending on usage:

    1. enums:

      • only possible for integer values
      • properly scoped / identifier clash issues handled nicely, particularly in C++11 enum classes where the enumerations for enum class X are disambiguated by the scope X::
      • strongly typed, but to a big-enough signed-or-unsigned int size over which you have no control in C++03 (though you can specify a bit field into which they should be packed if the enum is a member of struct/class/union), while C++11 defaults to int but can be explicitly set by the programmer
      • can't take the address - there isn't one as the enumeration values are effectively substituted inline at the points of usage
      • stronger usage restraints (e.g. incrementing - template <typename T> void f(T t) { cout << ++t; } won't compile, though you can wrap an enum into a class with implicit constructor, casting operator and user-defined operators)
      • 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. Each function's object code could be identical (ignoring address offsets), but I wouldn't expect a compiler/linker to eliminate the unnecessary copies, though you could check your compiler/linker if you care.
      • even with typeof/decltype, can't expect numeric_limits to provide useful insight into the set of meaningful values and combinations (indeed, "legal" combinations aren't even notated in the source code, consider enum { A = 1, B = 2 } - is A|B "legal" from a program logic perspective?)
      • the enum's typename may appear in various places in RTTI, compiler messages etc. - possibly useful, possibly obfuscation
      • you can't use an enumeration without the translation unit actually seeing the value, which means enums in library APIs need the values exposed in the header, and make and other timestamp-based recompilation tools will trigger client recompilation when they're changed (bad!)

    1. 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.
      • most similar to a non-const value, which minimises work and impact if switching between the two
      • value can be placed inside the implementation file, allowing a localised recompile and just client links to pick up the change

    1. #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.
        • while the trumping best-practice allows template parameter identifiers to be single-character uppercase letters (possibly followed by a number), other use of identifiers without lowercase letters is conventionally reserved for and expected of preprocessor defines (outside the OS and C/C++ library headers). This is important for enterprise scale preprocessor usage to remain manageable. 3rd party libraries can be expected to comply. Observing this implies migration of existing consts or enums to/from defines involves a change in capitalisation, and hence requires edits to client source code rather than a "simple" recompile. (Personally, I capitalise the first letter of enumerations but not consts, so I'd be hit migrating between those two too - maybe time to rethink that.)
      • more compile-time operations possible: string literal concatenation, stringification (taking size thereof), concatenation into identifiers
        • downside is that given #define X "x" and some client usage ala "pre" X "post", if you want or need to make X a runtime-changeable variable rather than a constant you force edits to client code (rather than just recompilation), whereas that transition is easier from a const char* or const std::string given they already force the user to incorporate concatenation operations (e.g. "pre" + X + "post" for string)
      • can't use sizeof directly on a defined numeric literal
      • 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
      • you can test for existence and value in #if statements for conditionally including code (more powerful than a post-preprocessing "if" as the code need not be compilable if not selected by the preprocessor), use #undef-ine, redefine etc.
      • substituted text has to be exposed:
        • in the translation unit it's used by, which means macros in libraries for client use must be in the header, so make and other timestamp-based recompilation tools will trigger client recompilation when they're changed (bad!)
        • or on the command line, where even more care is needed to make sure client code is recompiled (e.g. the Makefile or script supplying the definition should be listed as a dependency)

    My personal opinion:

    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).

    0 讨论(0)
  • 2020-11-22 13:31

    Always prefer to use the language features over some additional tools like preprocessor.

    ES.31: Don't use macros for constants or "functions"

    Macros are a major source of bugs. Macros don't obey the usual scope and type rules. Macros don't obey the usual rules for argument passing. Macros ensure that the human reader sees something different from what the compiler sees. Macros complicate tool building.

    From C++ Core Guidelines

    0 讨论(0)
  • 2020-11-22 13:34

    As a rather old and rusty C programmer who never quite made it fully to C++ because other things came along and is now hacking along getting to grips with Arduino my view is simple.

    #define is a compiler pre processor directive and should be used as such, for conditional compilation etc.. E.g. where low level code needs to define some possible alternative data structures for portability to specif hardware. It can produce inconsistent results depending on the order your modules are compiled and linked. If you need something to be global in scope then define it properly as such.

    const and (static const) should always be used to name static values or strings. They are typed and safe and the debugger can work fully with them.

    enums have always confused me, so I have managed to avoid them.

    0 讨论(0)
  • 2020-11-22 13:35

    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

    0 讨论(0)
提交回复
热议问题