21 October 2011

Conditional initialization of 'const' variables

Declaring a variable 'const' if one is not going to change its value is a good practice, even more, not declaring it 'const' is a bad practice and may sometimes be error prone. The question is "how can we initialize a 'const' variable with different expressions based on some condition evaluation?". Let us have 'const' variables somewhere in our code initialized with a function call or just a constant expression:

const int val = 5; // initializing with const expression
const MyClass &ref = get_object_of_my_class();
// returning an object or a const reference
MyClass *const ptr = get_pointer();
// returning a const or non-const pointer

Now consider some piece of code has been changed and we need to update our part so that the previous behaviour is relevant only if some condition applies. Let's assume that the condition expression is evaluated and the value is returned by the following function:

bool condition_applies(); // the definition does not matter

So now the part of old code should look like this:

if (condition_applies()) {
    const int val = 5;
    const MyClass &ref = get_object_of_my_class();
    MyClass *const ptr = get_pointer();
}
// But for the new case when condition does not
// apply we shall have an else statement
else {
    const int val = 1;
    const MyClass &ref = get_another_object_of_my_class();
    MyClass *const ptr = get_another_pointer();
}


Now if we leave the rest of code intact, the variables 'val', 'ref' and 'ptr' will be undeclared in the scope they are used as they are declared and destroyed in the scope of the most nested block they appear in. The obvious solution for this is to move the declarations out of 'if' statement, but in this case we will have two problems. The first one is that the references must be initialized at the time of declaration, and the second is that we cannot keep the 'const' qualifier of the pointer and object as we should assign them different expressions depending on the condition.


int val;
MyClass &ref; // error: reference must be initialized
MyClass *ptr;

if (condition_applies()) {
    val = 5;
    ptr = get_pointer();
} else {
    val = 1;
    ptr = get_another_pointer();
}

How to be then? We can take advantage of one of the most basic operators yet coming from 'C' language, the ternary operator '? :'. Both problems can be resolved with this operator as it always evaluates only one expression and returns it, so the assignable variable does not have to be changed after initialization:


const bool cnd_app = condition_applies();
// note using const wherever possible

const int val = (cnd_app)? 5 : 1;
const MyClass &ref = (cnd_app) ? get_object_of_my_class()
                               : get_another_object_of_my_class();
MyClass *const ptr = (cnd_app) ? get_pointer()
                               : get_another_pointer();


Now all the variables are declared in the same scope as in the first version of code and all the 'const' qualifiers are kept, which was the main task in question. I hope this piece of writing will be useful to at least one person.