C ++使用`const int`作为循环变量?

时间:2011-05-12 04:00:44

标签: c++ conditional-compilation

我想编写有条件编译的代码,并根据以下两种情况编写:

CASE_A:

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

CASE_B:(==!CASE_A)

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

也就是说,在情况A中,我希望有超过可变正常回路i,但在情况B中,我想限制本地作用域变量i,以仅一种特殊情况(这里指定作为i = 0)。显然,我可以写一些内容:

    for(int i = (CASE_A ? 1 : 0); i <= (CASE_A ? 10 : 0); ++i){
         // do something
    }

不过,我不喜欢这个设计,因为它不会让我趁const声明中的特殊情况B.这样的声明可能会允许大量优化的,因为这循环的主体通过其i的常量值替换i的潜在编译时间大大受益。

期待社区提供有关如何有效实现这一目标的任何提示。

谢谢!

编辑:

可以在编译时评估CASE_A和CASE_B。

i未作为参考传递

const是不能在体内(否则{{1}}就没有意义)重新评估,但我不知道,编译器将通过证明这种努力

4 个答案:

答案 0 :(得分:7)

假设你没有过度简化你的例子,那应该没关系。假设CASE_A可以在编译时进行评估,代码为:

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

将生成与:

相同的机器代码
const int i = 0;
do_something_with( i );

对于任何体面的编译器(当然,优化已打开)。

在研究这个问题时,我发现这里有一个很好的观点。如果i通过指针或引用传递给函数,则编译器不能认为它不会更改。即使指针或引用是const,也是如此! (因为const可以在函数中抛弃。)

答案 1 :(得分:2)

似乎是一个明显的解决方案:

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

答案 2 :(得分:1)

如果您的CASE_B / CASE_B确定可以表示为编译时常量,那么您可以使用类似下面的内容以一种漂亮,可读的格式执行您想要的操作(这只是您使用{{{ 1}}运算符用于?:循环初始化和条件):

for

这清楚地表明enum { kLowerBound = (CASE_A ? 1 : 0), kUpperBound = (CASE_A ? 10 : 0) }; for (int i = kLowerBound; i <= kUpperBound; ++i) { // do something } 循环边界是编译时常量 - 请注意,我认为即使在for表达式直接用于{?:表达式,今天大多数编译器也没有问题。 {1}}声明的控制条款。但是,我认为使用枚举使人们阅读代码更加明显。

同样,今天任何有价值的编译器都应该认识到for在循环中是不变的,并且在i情况下也确定循环永远不会迭代。使CASE_B i无法使编译器的优化可能性受益。

如果您确信如果consti,编译器可能能够更好地进行优化,那么简单的修改可以提供帮助:

const

我怀疑这会对编译器有多大帮助(但检查它的输出 - 我可能是错的)如果for (int ii = kLowerBound; ii <= kUpperBound; ++ii) { const int i = ii; // do something } 未被修改或者其地址被采用(即使将其作为参考传递)。但是,它可能有助于确保i未被不恰当地修改或通过循环中的引用/地址传递。

另一方面,如果在其上使用i修饰符,您可能会看到编译器产生的优化带来的好处 - 在const的地址被采用的情况下或{ {1}}被抛弃,编译器仍被允许将i视为未在其生命周期内被修改。抛弃const的东西可能造成的任何修改都是未定义的行为,因此允许编译器忽略它们可能发生的行为。当然,如果您有可能执行此操作的代码,那么您需要比优化更大的担忧。因此,确保i没有“背后”修改尝试比仅仅将const标记为i'以进行优化更为重要,更重要的是使用{{1}可能会帮助您确定是否进行了修改(但请记住,强制转换可以继续隐藏)。

答案 3 :(得分:0)

我不太确定这是你正在寻找的,但我正在使用vanilla FOR循环的这个宏版本强制循环计数器为const以捕获它的任何修改在身体

#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)

用法:

#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;
}