Is it better to remove the "const" before the "primitive" types that are used as function parameters in the header?

In the process of checking the code, one of my colleagues mentioned to me that the "const" before the "primitive types" used as a function parameter in the header does not make sense, and he recommended deleting these "constants". In such cases, he suggested using "const" only in the source file. Primitive types mean types such as "int", "char", "float", etc.

The following is an example.

example.h

int ProcessScore(const int score); 

example.cc

 int ProcessScore(const int score) { // Do some calculation using score return some_value; } 

His suggestion does the following:

example.h

 int ProcessScore(int score); // const is removed here. 

example.cc

 int ProcessScore(const int score) { // Do some calculation using score return some_value; } 

But I'm a little confused. Usually, the user will only look at the header, so if there is a mismatch between the header and the source file, this can cause confusion.

Can anyone give some advice on this?

+42
c ++ primitive-types header const
Sep 19 '17 at 5:30
source share
4 answers

For all types (not just primitives), top-level qualifiers are ignored in the function declaration. So, the following four all declare the same function:

 void foo(int const i, int const j); void foo(int i, int const j); void foo(int const i, int j); void foo(int i, int j); 

However, the const specifier is not ignored inside the function body. There it can affect constant correctness. But this is a granularity of the function. So the general consensus is this:

  • Leave const from the declaration. It is simply cluttered and does not affect how clients call this function.

  • Leave the constant in the definition if you want the compiler to catch any random modification of the parameter.

+63
Sep 19 '17 at 5:47 on
source share

A function parameter declared as const and const remains unchanged when the overload resolution is reached. So for example functions

 void f(int); void f(const int); 

match and cannot be defined together. As a result, it is better not to use const in the declaration for parameters in general, to avoid possible duplication. (I'm not talking about a constant reference or a constant pointer - because the constant modifier is not the top level.)

Here is the exact quote from the standard.

After creating a list of parameter types, any top-level cv-qualifiers that change the parameter type are deleted when the function type is formed. The resulting list of converted parameter types and the presence or absence of ellipsis or a package of function parameters is a list of type parameters. [Note: this conversion does not affect parameter types. For example, int(*)(const int p, decltype(p)*) and int(*)(int, const int*) are identical types. - final note]

The usefulness of a constant in defining a function is debatable: the reasoning behind it is the same as using const to declare a local variable - it shows other programmers reading the code that this value will not be changed inside the function.

+40
Sep 19 '17 at 5:42 on
source share

Follow the guidelines in the code review.

Using const for value arguments has no semantic meaning - it is only significant (potentially) to implement your function - and even then I would say that it is not necessary.

edit: To be clear: the prototype of your functions is the public interface for your function. What const means is a guarantee that you will not change the links.

 int a = 7; do_something( a ); void do_something( int& x ); // 'a' may be modified void do_something( const int& x ); // I will not modify 'a' void do_something( int x ); // no one cares what happens to x 

Using const is something similar to TMI - it doesnโ€™t matter anywhere other than inside the function whether "x" changes.

edit2: I also really like the information in StoryTellers Answers

+15
Sep 19 '17 at 5:43 on
source share

Like many other people, from an API point of view, all are equivalent and equal for overload resolution:

 void foo( int ); void foo( const int ); 

But the best question is whether this gives any semantic meaning to the consumer of this API or whether it provides any coercion to good behavior from the implementation developer.

Without any well-defined developer coding rules that directly define this, const scalar arguments have no obvious semantic meaning.

From the consumer: const int does not change your input. It can still be literal, or it can be from another variable (both const and non- const )

From the developer: const int imposes a restriction on the local copy of the variable (in this case, the function argument). It just means changing the argument, you take another copy of the variable and change it.

When a function that takes an argument is called, a copy of this argument is created on the stack for the called function. This gives the function a local copy of the argument for its entire area, which can then be changed, used for calculations, etc. - without affecting the original input transferred to the call. In fact, this provides a local variable argument for its input.

Marking the argument as const , it simply means that this copy cannot be modified; but he does not prohibit the developer from copying it and making changes to this copy. Since it was a copy from the very beginning, it does not provide all of this from within the implementation - and ultimately does not matter much from the consumer's point of view.

This contradicts passing by reference, where the reference to int& semantically different from const int& . The first is able to mutate its contribution; the latter can only observe the input (provided that the implementation is not const_cast const -ness), but allows you to ignore this possibility); thus, const -ness on links has an implied semantic meaning.

This does not greatly benefit the public API; and (imo) introduces unnecessary restrictions on the implementation. As an arbitrary, far-fetched example, a simple function like:

 void do_n_times( int n ) { while( n-- > 0 ) { // do something n times } } 

Now you will need to write using an unnecessary copy:

 void do_n_times( const int n ) { auto n_copy = n; while( n_copy-- > 0 ) { // do something n times } } 

Regardless of whether const scalars are used in the public API, one key thing must be consistent with the design. If the API randomly switches between using scanning const arguments using unscanned const scalars, this can cause confusion as to whether this is implied for any implied value to the consumer.

TL; DR: const scalar types in the public API do not convey semantic meaning unless explicitly defined by your own recommendations for your domain.

+6
Sep 19 '17 at 21:03 on
source share



All Articles