折叠 C++ 中的断言;班级?

发布于 2024-08-06 09:34:18 字数 766 浏览 4 评论 0原文

因此,在非类类型的情况下,我可以这样做:

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 进行编译时)

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)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

俏︾媚 2024-08-13 09:34:18

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

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

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

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?

孤云独去闲 2024-08-13 09:34:18

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

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

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.

梦在深巷 2024-08-13 09:34:18

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

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

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.

淡淡的优雅 2024-08-13 09:34:18

-O2 似乎对我有用:

% 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

-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
肥爪爪 2024-08-13 09:34:18

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

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

请记住,断言的一般思想是它的失败不应该发出诸如输入错误之类的信号。它只能用来表示程序本身存在根本问题——某个地方有错误的逻辑,或者某个顺序上的东西。

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

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

If you want an assert that you're sure gets folded away, you might want to look up boost::static_assert.

Right now, I'm rather bothered by the basic notion of the design: you're allowing the user of the class to supply a value, but then asserting that it must be one particular value. If only one value is right, why have them supply it at all? Why not just use the right value and be done with it?

Keep in mind that the general idea of an assert is that its failure should NOT signal something like incorrect input. It should only be used to signal a fundamental problem with the program itself -- that there's faulty logic somewhere, or something on that order.

On the other hand, maybe you're dealing with a situation where different values are allowed, but you want to take different actions (or implement the same actions differently) for one particular value. To get the assert folded away, it's obviously going to have to be a compile-time constant.

That being the case, I'd consider creating a template, with that value as a non-type template parameter. Specialize the template for the value you want to implement specially. This gives roughly the effect you seem to want, but enforces the fact that the test for that value has to happen at compile time, not run time. Instead of writing run-time code and hoping the compiler's smart enough to handle it at compile time, you write a compile-time match explicitly.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文