tags:

views:

191

answers:

5

So, in a non-class type of situation, I can do something like this:

int val_to_check = 0;

int some_func(int param) {
  assert(val_to_check == 0);
  return param*param+param;
}

int main() {
  printf("Val: %i\n", some_func(rand()));
  return 0;
}

If val_to_check is declared const instead, the assertion can be folded away by the compiler.

I'm curious if it's possible to get a similar constant folding with a member variable of a class. For example, can I do something like:

class Test {
public:
  Test(int val) : val_(val) {}
  int some_func(int param) {
     assert(val_ == 0);
     return param*param+param;
   }
private:
  const int val_;
};

So val_ must be known when the class is defined, a-la:

  Test my_test(0);
  printf("Val: %i\n", my_test.some_func(rand()));

(I know these are contrived examples). It seems like it should be possible to fold the assertions away sometimes, but the simple examples I've tested don't seem to do it. The best I've gotten is moving the assertion code to the end of the function (when compiling w/ -O3)

+2  A: 

In the class example you provided, there's no way for the compiler to assume the constant is zero because you have two runtime variables:

  • Your const int val_ is only constant for each instance of the class so it can never optimise the class's function code since it must cater for every case.
  • The example instantiation doesn't provide a literal constant, it provides the result of rand() which is variable. It may be possible for it to optimise it out if it knows the ONLY val_ ever being provided to all instances of that class is zero.

Have you tried providing a constant to the constructor to see if it optimises it out?

Nick Bedford
Ah that makes sense that it would still have to leave the code able to handle each case. I did try initializing it with a constant, to no avail, presumable because the code's left in case you link with something else that uses it.
gct
+2  A: 

In C++, there is the notion of a "constant expression" (5.19). This is a literal expression, an arithmetic expression only involving constant expressions, or a value of a variable initialized statically with a constant expression. The compiler is able to determine the value of such an expression at compile time.

In your case, val_ is not a "constant expression", since it may have different values depending on the object it is a member of. For the out-of-line version of some_func, you probably agree that the compiler cannot know that it is a constant. For the inline version, it would be possible to determine the value of val_, assuming you also have the complete source code of all constructors analyzed. Whether the compiler is able to make this analysis is a quality-of-implementation issue - your compiler apparently is not able to.

Martin v. Löwis
A: 

val_ is const for the instance, not for all instances. If you actually want it to be the same for all instances, then you can make it static const and that would allow the compiler to optimize it out which is really what is happening in your first example with a global made const.

As a side note, your examples are subject to integer overflow.

John Cavan
A: 

-O2 seems to do the trick for me:

% cat foo.cxx

 #include <cstdio>
 #include <cstdlib>
 #include <cassert>

 class Test {
   public:
     Test(int val) : val_(val) {}
     int some_func(int param) {
       assert(val_ == 0);
       return param*param+param;
     }
   private:
     const int val_;
 };

 int main() {
   Test my_test(0);
   printf("Val: %d\n", my_test.some_func(rand()));
   return 0;
 }

 % g++ -S foo.cxx && grep assert foo.s ; echo $?
         .ascii "%s:%u: failed assertion `%s'\12\0"
 0

 % g++ -O2 -S foo.cxx && grep assert foo.s ; echo $?
 1
Logan Capaldo
Doesn't do the trick for me. `__assert_fail()` call persists in both files.
Pavel Shved
A: 
Jerry Coffin