在C ++类中折叠断言?
-
18-09-2019 - |
题
因此,在非类型的情况下,我可以做这样的事情:
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()));
(我知道这些是人为的例子)。似乎有时应该可以将断言折叠起来,但是我测试过的简单示例似乎并没有这样做。我得到的最好的是将主张代码移至函数末尾(编译W/ -O3时)
解决方案
在您提供的类示例中,编译器无法假设常数为零,因为您有两个运行时变量:
- 您的
const int val_
仅适用于类的每个实例,因此它永远无法优化类的功能代码,因为它必须适合每种情况。 - 示例实例化无法提供字面常数,它提供了
rand()
这是可变的。它 可能 如果知道唯一的话,它可以将其优化val_
曾经向该类别的所有实例提供为零。
您是否尝试过向构造函数提供常数,以查看它是否优化了它?
其他提示
在C ++中,有一个“常数表达式”的概念(5.19)。这是一种字面的表达,仅涉及恒定表达式的算术表达,或具有恒定表达式的可变初始化的可变初始化的值。编译器能够在编译时确定这种表达式的值。
在您的情况下 val_
不是“恒定表达式”,因为它可能具有不同的值,具体取决于其是成员的对象。对于离线版本的 some_func
, ,您可能同意编译器不知道这是一个常数。对于内联版本,可以确定 val_
, ,假设您还具有所有分析的所有构造函数的完整源代码。编译器是否能够进行此分析是一个实施质量问题 - 您的编译器显然无法做到。
val_是该实例的const,而不是所有实例。如果您实际上希望在所有情况下都相同,那么您可以使其成为静态常量,这将使编译器可以优化它,这实际上是您使用全局制造的const的第一个示例中发生的事情。
附带说明,您的示例可能会受到整数溢出的约束。
-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
如果您想断言自己肯定会被折叠起来,则可能需要查找boost :: static_assert。
目前,我对设计的基本概念感到困扰:您允许班级用户提供一个值,但随后断言它必须是一个特定的值。如果只有一个值是正确的,为什么它们根本提供它?为什么不只是使用正确的价值并可以完成呢?
请记住,断言的总体想法是,其失败不应表示不正确的输入。它仅应用于向程序本身的基本问题发出信号 - 某处存在错误的逻辑或该顺序的内容。
另一方面,也许您正在处理允许不同值的情况,但是您想对一个特定值采取不同的操作(或以不同的方式实施相同的动作)。为了使断言折叠起来,显然必须是一个编译时间常数。
在这种情况下,我会考虑创建一个模板,该值作为非类型模板参数。专门针对要专门实现的值进行专门化模板。这大致赋予了您似乎想要的效果,但是强制执行该值的测试必须在编译时间而不是运行时间进行。您没有编写运行时代码并希望编译器的智能足够聪明,可以在编译时处理它,而是明确编写编译时间匹配。