This style guide has been useful to me, but I came across rule #5:
In general, the use of such constants should be minimized. In many cases implementing the value as a method is a better choice:
int getMaxIterations() // NOT: MAX_ITERATIONS = 25
{
return 25;
}
I understand the reasoning from a style point of view: Not only do you do away with the "shouting" constant declarations, but you're also reducing the number of language constructs in use (forgive me if this is incorrect terminology), making the program easier to understand.
However, does this approach have a derogatory effect on the compiler, or are modern compilers (or, in fact, older compilers..) able to look-ahead enough to determine that your getMaxIterations function is returning the same number every time?
Indeed, and on second thoughts, does the compiler even need to look ahead? The style guide suggests the method approach is better than using a constant value, would I be right in guessing this is because the "constant" value does not need to be held in memory after its use in whatever scope it's in has been completed?
In summary, my question is: Is the use of constant values discouraged and, if so, why?
(And for bonus points, what are the technical differences between declaring a constant value as a method and as a constant?)
This is inlined with most compilers.
With that said, the style guide is making a point. It says instead of doing this:
#define FOO_MAX_ITERATIONS 25
struct Foo {
void do_it() { for(int i = 0; i < FOO_MAX_ITERATIONS; i++) iterate(); }
};
Should instead be this:
struct Foo {
int getMaxIterations() { return 25; }
void do_it() { for(int i = 0; i < getMaxIterations(); i++) iterate(); }
};
As you can see it's much more consistent and readable in the long run, and good for design down the road, such as when you inherit from the class. Later on, you may want getMaxIterations() to be modified at run-time, and you won't have to do an ugly hack like #define FOO_MAX_ITERATIONS someMethod().
If you're using any sane C++11 compiler (i.e. not Visual Studio), such functions should additionally be declared as so:
struct Foo {
constexpr int getMaxIterations() { return 25; }
void do_it() { for(int i = 0; i < getMaxIterations(); i++) iterate(); }
};
Note constexpr in the declaration of getMaxIterations() there. That tells the C++11 compiler that it is a "constant expression," and it can be evaluated at compile time. By doing this, it can directly replace getMaxIteratsion() with 25 before compiling, among many, many other things, such as allowing you to use it in other compile time declarations.
One reason is what Bertrand Meyer calls "the principle of uniform reference" (IIRC).
He used the example of a bank account and the current balance. Should this be a data member or function/method? Meyer argues that this type of decision is likely to change, possibly several times, over the lifetime of a software project. Therefore, even if the balance is currently represented as a data member, you should wrap it in a getter. Then, even if the implementation has to change, the interface won't have to change ... and therefore clients of this class are insulated from the implementation change.
Put another way, even though this is a constant now, you may find yourself at a point where it's something you need to calculate.
And, as others have noted, any modern compiler is (hopefully) smart enough that they'll inline the method implementation and you won't incur a performance penalty from making it a method.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With