我有一堆如下代码:
int sign(MyEnum e)
{
switch(e)
{
case A:
case B:
return 1;
case C:
case D:
return -1;
default:
throw std::runtime_error("Invalid enum value");
}
}
int f(int a, int b, int c, MyEnum e)
{
const int sign = sign(e);
const int x = a * b - sign * c;
const int y = a + sign * c;
return x / y;
}
这里的算术只是一个例子。实际的代码更复杂,但要点是
sign
是 -1 或 1,具体取决于枚举值,并且我们进行了一系列计算,其中各种事物乘以 sign
。 (编辑:枚举值在编译时未知。)
我希望优化此代码,就像我编写了如下内容一样:
int f(int a, int b, int c, MyEnum e)
{
switch(e)
{
case A:
case B:
{
const int x = a * b - c;
const int y = a + c;
return x / y;
}
case C:
case D:
{
const int x = a * b + c;
const int y = a - c;
return x / y;
}
default:
throw new std::runtime_error("Invalid enum value");
}
}
当然,我实际上并不想编写这样的所有代码,因为这是测试和维护的噩梦。
使用编译器资源管理器,看起来
sign
中的异常可能是这里的问题;如果我有“默认”情况返回,例如-1,那么我就得到了我想要的。但我希望这里有一些安全。
问题:
-O3
编译这个方法会产生该方法的两个克隆,其中一个执行我想要的操作,尽管我不知道哪一个实际上会运行。我可以为此提供提示吗?
-O3
处编译所有。我可以仅针对特定代码块打开优化,或者鼓励编译器进行优化吗?
编辑:由于我(显然)不理解手头的所有问题,所以我可能没有给它一个很好的标题。如果您知道自己在做什么,请随时编辑。
template <int sign>
int f(int a, int b, int c)
{
const int x = a * b - sign * c;
const int y = a + sign * c;
return x / y;
}
int f(int a, int b, int c, MyEnum e)
{
const int sign = sign(e);
if (sign == 1) return f<1>(a, b, c);
else return f<-1>(a, b, c);
}
这样,您可以保持所需的安全性(以异常的形式),但随后将结果信息转换为编译时值,编译器可以使用该值进行优化。
正如
Chris在评论中指出的那样,如果sign
仅用于切换
c
的符号,您可以完全摆脱模板,只需在调用时翻转
c
的符号即可:
int f(int a, int b, int c)
{
const int x = a * b - c;
const int y = a + c;
return x / y;
}
int f(int a, int b, int c, MyEnum e)
{
const int sign = sign(e);
if (sign == 1) return f(a, b, c);
else return f(a, b, -c);
}
int sign(MyEnum)
功能没有被其他翻译单元使用,那么可以将其标记为
static
。在此上下文中,
static
表示该函数是翻译单元的本地函数,并且不会链接到该翻译单元之外。 (关键字
static
在 C++ 中具有不同的含义,具体取决于使用的上下文。)这允许优化器执行更多优化,并可能完全消除该函数(假设启用了优化)。
int f(int a, int b, int c)
{
const int x = a * b - c;
const int y = a + c;
return x / y;
}
enum MyEnum {
A, B, C, D
};
// Template definition with default behavior.
template<MyEnum e> int correct_c(int c) { throw std::runtime_error("Invalid enum value"); return 0; };
// Specialized templates according to the value of e.
template<> int correct_c<A>(int c) { return c; }
template<> int correct_c<B>(int c) { return c; }
template<> int correct_c<C>(int c) { return -c; }
template<> int correct_c<D>(int c) { return -c; }
用途:
void test() {
int rA = f(7, 2, correct_c<A>(3));
int rB = f(7, 2, correct_c<B>(3));
int rC = f(7, 2, correct_c<C>(3));
int rD = f(7, 2, correct_c<D>(3));
int rUnknown = 0;
try {
rUnknown = f(7, 2, correct_c<static_cast<MyEnum>(4)>(3));
} catch (const std::runtime_error& e) {
cout << "Runtime error caught \"" << e.what() << "\"\n";
}
cout << "Results: \n"
"rA: " << rA << "\n"
"rB: " << rB << "\n"
"rC: " << rC << "\n"
"rD: " << rD << "\n"
"rUnknown: " << rUnknown << '\n';
}
(请注意,我将 c
的逻辑移到了
f
之外,以避免逻辑内聚。) 编译器会将调用内联到
correct_c
。这也将阻止编译非枚举值,除非您明确地强制转换它们。
如果您需要支持
e
的运行时评估,您可以编写一个特定函数,该函数将
MyEnum
值作为参数而不是模板参数,并将其路由到适当的专用模板函数。
int correct_c(int c, MyEnum e) {
switch (e) {
case A: return correct_c<A>(c);
case B: return correct_c<B>(c);
case C: return correct_c<C>(c);
case D: return correct_c<D>(c);
default: throw std::runtime_error("Invalid enum value");
}
}