1

所以,在非类的情况下,我可以做这样的事情:

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

如果改为val_to_check声明const,则编译器可以折叠该断言。

我很好奇是否可以使用类的成员变量获得类似的常量折叠。例如,我可以执行以下操作:

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

所以在定义类时必须知道 val_,a-la:

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

(我知道这些都是人为的例子)。似乎有时应该可以将断言折叠起来,但我测试过的简单示例似乎并没有这样做。我得到的最好的是将断言代码移动到函数的末尾(使用 -O3 编译时)

4

5 回答 5

2

在您提供的类示例中,编译器无法假定常量为零,因为您有两个运行时变量:

  • const int val_对于类的每个实例都是唯一的,因此它永远不能优化类的功能代码,因为它必须满足每种情况。
  • 示例实例化不提供文字常量,它提供的结果rand()是可变的。如果它知道提供给该类的所有实例的唯一值为零,它可能会对其进行优化。val_

您是否尝试过为构造函数提供一个常量以查看它是否对其进行了优化?

于 2009-09-28T01:48:53.507 回答
2

在 C++ 中,存在“常量表达式”(5.19)的概念。这是一个文字表达式,一个只涉及常量表达式的算术表达式,或者一个用常量表达式静态初始化的变量的值。编译器能够在编译时确定此类表达式的值。

在您的情况下,val_它不是“常量表达式”,因为它可能具有不同的值,具体取决于它所属的对象。对于 的外联版本some_func,您可能同意编译器无法知道它是一个常量。对于内联版本,可以确定 的值val_,假设您还分析了所有构造函数的完整源代码。编译器是否能够进行这种分析是一个实现质量问题——你的编译器显然不能。

于 2009-09-28T01:53:02.630 回答
0

val_ 是实例的常量,而不是所有实例。如果您实际上希望它对所有实例都相同,那么您可以将其设为静态 const,这将允许编译器对其进行优化,这实际上是在您的第一个示例中使用全局生成的 const 发生的情况。

作为旁注,您的示例可能会出现整数溢出。

于 2009-09-28T01:58:19.557 回答
0

-O2 似乎对我有用:

% 猫 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
于 2009-09-28T02:00:59.690 回答
0

如果你想要一个肯定会被折叠的断言,你可能需要查找 boost::static_assert。

现在,我对设计的基本概念感到相当困扰:您允许类的用户提供一个值,但随后断言它必须是一个特定的值。如果只有一个值是正确的,为什么还要他们提供呢?为什么不直接使用正确的值并完成它呢?

请记住,断言的一般概念是它的失败不应发出错误输入之类的信号。它应该只用于表示程序本身的一个基本问题——某处存在错误逻辑,或者该顺序中的某些东西。

另一方面,也许您正在处理允许不同值的情况,但您想对一个特定值采取不同的操作(或以不同的方式实现相同的操作)。为了让断言折叠起来,它显然必须是一个编译时常量。

在这种情况下,我会考虑创建一个模板,将该值作为非类型模板参数。为您要专门实现的值专门化模板。这大致给出了您似乎想要的效果,但强制执行该值的测试必须在编译时发生,而不是在运行时发生。与其编写运行时代码并希望编译器足够聪明以在编译时处理它,不如明确地编写编译时匹配。

于 2009-09-28T03:09:41.470 回答