How Many and Which Are the Uses of "Const" in C++

How many and which are the uses of const in C++?

Trying to collect some uses:

Binding some temporary to reference-to-const, to lengthen its lifetime. The reference can be a base - and the destructor of it doesn't need to be virtual - the right destructor is still called:

ScopeGuard const& guard = MakeGuard(&cleanUpFunction);

Explanation, using code:

struct ScopeGuard { 
~ScopeGuard() { } // not virtual
};

template<typename T> struct Derived : ScopeGuard {
T t;
Derived(T t):t(t) { }
~Derived() {
t(); // call function
}
};

template<typename T> Derived<T> MakeGuard(T t) { return Derived<T>(t); }

This trick is used in Alexandrescu's ScopeGuard utility class. Once the temporary goes out of scope, the destructor of Derived is called correctly. The above code misses some small details, but that's the big deal with it.


Use const to tell others methods won't change the logical state of this object.

struct SmartPtr {
int getCopies() const { return mCopiesMade; }
};

Use const for copy-on-write classes, to make the compiler help you to decide when and when not you need to copy.

struct MyString {
char * getData() { /* copy: caller might write */ return mData; }
char const* getData() const { return mData; }
};

Explanation: You might want to share data when you copy something as long as the data of the originally and the copie'd object remain the same. Once one of the object changes data, you however need now two versions: One for the original, and one for the copy. That is, you copy on a write to either object, so that they now both have their own version.

Using code:

int main() {
string const a = "1234";
string const b = a;
// outputs the same address for COW strings
cout << (void*)&a[0] << ", " << (void*)&b[0];
}

The above snippet prints the same address on my GCC, because the used C++ library implements a copy-on-write std::string. Both strings, even though they are distinct objects, share the same memory for their string data. Making b non-const will prefer the non-const version of the operator[] and GCC will create a copy of the backing memory buffer, because we could change it and it must not affect the data of a!

int main() {
string const a = "1234";
string b = a;
// outputs different addresses!
cout << (void*)&a[0] << ", " << (void*)&b[0];
}

For the copy-constructor to make copies from const objects and temporaries:

struct MyClass {
MyClass(MyClass const& that) { /* make copy of that */ }
};

For making constants that trivially can't change

double const PI = 3.1415;

For passing arbitrary objects by reference instead of by value - to prevent possibly expensive or impossible by-value passing

void PrintIt(Object const& obj) {
// ...
}

How do I best use the const keyword in C?

const is typed, #define macros are not.

const is scoped by C block, #define applies to a file (or more strictly, a compilation unit).

const is most useful with parameter passing. If you see const used on a prototype with pointers, you know it is safe to pass your array or struct because the function will not alter it. No const and it can.

Look at the definition for such as strcpy() and you will see what I mean. Apply "const-ness" to function prototypes at the outset. Retro-fitting const is not so much difficult as "a lot of work" (but OK if you get paid by the hour).

Also consider:

const char *s = "Hello World";
char *s = "Hello World";

which is correct, and why?

Why do we not use const more often?

In most cases it no longer makes a difference.

Often enough the compiler will be able to deduce that the variable is actually read only, and treat it as a constant during optimization.

Either way, you usually use const consequently on things like const char *string in order to avoid accidentally modifying the parameter.

Since all parameters are passed by value, this isn't something which can't happen for an integer. Only for pointers the pointer itself is the value.

Actually, const char *string isn't fully constant either yet. Only the chars the pointer is pointing to is, but the pointer as a variable isn't. It would only become when specifying it as const char * const string. Now the string variable is completely constant for the scope of the function.

What's the correct way to use const in C++?

Where you use const depends on the purpose of the function. As James suggests in his comment (which is worth putting as an answer), put const anywhere you can:

If the function is intended to modify state within it's object instance, don't put const at the end of the signature.

If the function is intended to modify one of it's reference or pointer parameters, don't put const on the parameter.

If the variable referenced by a pointer or reference should be modified, don't put const on the type (remember, const applies to the part of the definition immediately prior).

If the returned reference/pointer references a variables that should not be changed by the received, do put const on the type.

Answering the examples given in the question is impossible without knowing the purpose of the functions. My tendency would be to use string ToString() const and char* ToString() const, with very clear documentation on who is responsible for deleteing the char*.


As an extra note, const char* and char const* are identical (pointer to unmodifiable characters). char* const, on the other hand, is an unmodifiable pointer to modifiable characters.

Use const wherever possible in C++?

C++ FAQ:

If you find ordinary type safety helps you get systems correct (it
does; especially in large systems), you'll find const correctness
helps also.

You should use const when you want to be sure not to change variable accidentally or intentionally. Some constants (globals and class static, strings & integers, but not variables with nontrivial constructor) can be placed in read-only parts of the executable, therefore result in segmentation fault if you try to write to it.

You should be explicit using const as a specifier on functions that follow this principle as well as on function arguments. If you don't have to change actual argument, make it const. This doesn't limit the possible usages of such function, but extends them, because now they might be used on const arguments, and on const objects.

In declaration

const int* foo(const int* const&) const;

every const means something different and yes, obviously it should be used if it is needed.

Summary

Using const increases type-safety of your program.

C++ FAQ:

[18.3] Should I try to get things const correct "sooner" or "later"?

At the very, very, very beginning.

C++ const keyword - use liberally?

This, IMHO, is overusing.
When you say 'const int age,...' what you actually say is "you can't change even the local copy inside your function". What you do is actually make the programmer code less readable by forcing him to use another local copy when he wants to change age/pass it by non-const reference.

Any programmer should be familiar with the difference between pass by reference and pass by value just as any programmer should understand 'const'.

Defining constant in c with const keyword

Using the const type qualifier doesn't make something a constant. A constant in C has its own definition.

See § 6.7.3 ¶ 6 of the C11 standard for a description of the const keyword:

If an attempt is made to modify an object defined with a const-qualified type through use of an lvalue with non-const-qualified type, the behavior is undefined. If an attempt is made to refer to an object defined with a volatile-qualified type through use of an lvalue with non-volatile-qualified type, the behavior is undefined.

What you need there is a constant expression; see § 6.6 of C11 for details.

If what you're really wondering isn't "WTF is up with the const qualifier?" but rather what the right solution for your code is, the answer is likely to simply not specify the size of the array:

float user_array[] = {5.1, 7.2, 5.1, 8.45, 23.0, 67.123, 5.1};

This is generally considered good practice as it actually makes your code a bit more robust.



Related Topics



Leave a reply



Submit