开发者

C++ use `const int` as looping variable?

开发者 https://www.devze.com 2023-03-05 19:35 出处:网络
I want to write code that compiles conditionally and according to the following two cases: CASE_A: for(int i = 1; i <= 10; ++i){

I want to write code that compiles conditionally and according to the following two cases:

CASE_A:

    for(int i = 1; i <= 10; ++i){
        // do something...
    }

CASE_B: ( == !CASE_A)

    {
        const int i = 0;
        // do something...
    }

That is, in case A, I want to have a normal loop over variable i but, in case B, i want to restrict local scope variable i to only a special case (designated here as i = 0). Obviously, I could write something along the lines:

    for(int i = (CASE_A ? 1 : 0); i <= (CASE_A ? 10 : 0); ++i){
         // do somethin开发者_如何学运维g
    }

However, I do not like this design as it doesn't allow me to take advantage of the const declaration in the special case B. Such declaration would presumably allow for lots of optimization as the body of this loop benefits greatly from a potential compile-time replacement of i by its constant value.

Looking forward to any tips from the community on how to efficiently achieve this.

Thank you!

EDITS:

CASE_A vs CASE_B can be evaluated at compile-time.

i is not passed as reference

i is not re-evaluated in the body (otherwise const would not make sense), but I am not sure the compiler will go through the effort to certify that


Assuming, you aren't over-simplifying your example, it shouldn't matter. Assuming CASE_A can be evaluated at compile-time, the code:

for( int i = 0; i <= 0; ++i ) {
    do_something_with( i );
}

is going to generate the same machine code as:

const int i = 0;
do_something_with( i );

for any decent compiler (with optimization turned on, of course).

In researching this, I find there is a fine point here. If i gets passed to a function via a pointer or reference, the compiler can't assume it doesn't change. This is true even if the pointer or reference is const! (Since the const can be cast away in the function.)


Seems to be the obvious solution:

template<int CASE>
void do_case();

template<>
void do_case<CASE_A>()
{
  for(int i = 1; i <= 10; ++i){
    do_something( i );
  }
}

template<>
void do_case<CASE_B>()
{
  do_something( 0 );
}

// Usage
...
do_case<CURRENT_CASE>(); // CURRENT_CASE is the compile time constant


If your CASE_B/CASE_B determination can be expressed as a compile time constant, then you can do what you want in a nice, readable format using something like the following (which is just a variation on your example of using the ?: operator for the for loop initialization and condition):

enum {
    kLowerBound = (CASE_A ? 1 : 0),
    kUpperBound = (CASE_A ? 10 : 0)
};

for (int i = kLowerBound; i <= kUpperBound; ++i) {
    // do something
}

This makes it clear that the for loop bounds are compile time constants - note that I think most compilers today would have no problem making that determination even if the ?: expressions were used directly in the for statement's controlling clauses. However, I do think using enums makes it more evident to people reading the code.

Again, any compiler worth its salt today should recognize when i is invariant inside the loop, and in the CASE_B situation also determine that the loop will never iterate. Making i const won't benefit the compiler's optimization possibilities.

If you're convinced that the compiler might be able to optimize better if i is const, then a simple modification can help:

for (int ii = kLowerBound; ii <= kUpperBound; ++ii) {
    const int i = ii;

    // do something
}

I doubt this will help the compiler much (but check it's output - I could be wrong) if i isn't modified or has its address taken (even by passing it as a reference). However, it might help you make sure that i isn't inappropriately modified or passed by reference/address in the loop.

On the other hand, you might actually see a benefit to optimizations produced by the compiler if you use the const modifier on it - in the cases where the address of i is taken or the const is cast away, the compiler is still permitted to treat i as not being modified for its lifetime. Any modifications that might be made by something that cast away the const would be undefined behavior, so the compiler is allowed to ignore that they might occur. Of course, if you have code that might do this, you have bigger worries than optimization. So it's more important to make sure that there are no 'behind the back' modification attempts to i than to simply marking i as const 'for optimization', but using const might help you identify whether modifications are made (but remember that casts can continue to hide that).


I'm not quite sure that this is what you're looking for, but I'm using this macro version of the vanilla FOR loop which enforces the loop counter to be const to catch any modification of it in the body

#define FOR(type, var, start, maxExclusive, inc) if (bool a = true) for (type var##_ = start; var##_ < maxExclusive; a=true,var##_ += inc) for (const auto var = var##_;a;a=false)

Usage:

#include <stdio.h>

#define FOR(type, var, start, maxExclusive, inc) if (bool a = true) for (type var##_ = start; var##_ < maxExclusive; a=true,var##_ += inc) for (const auto var = var##_;a;a=false)

int main()
{
    FOR(int, i, 0, 10, 1) {
        printf("i: %d\n", i);
    }

    // does the same as: 
    for (int i = 0; i < 10; i++) {
        printf("i: %d\n", i);
    }

    // FOR catches some bugs:
    for (int i = 0; i < 10; i++) {
        i += 10; // is legal but bad
        printf("i: %d\n", i);
    }

    FOR(int, i, 0, 10, 1) {
        i += 10; // is illlegal and will not compile
        printf("i: %d\n", i);
    }
    return 0;
}
0

精彩评论

暂无评论...
验证码 换一张
取 消