如果我有两个不同的常量成员变量,都需要基于相同的函数调用进行初始化,是否有一种方法可以在不两次调用函数的情况下进行?
例如,分数类,其中分子和分母是常数。
int gcd(int a, int b); // Greatest Common Divisor
class Fraction {
public:
// Lets say we want to initialize to a reduced fraction
Fraction(int a, int b) : numerator(a/gcd(a,b)), denominator(b/gcd(a,b))
{
}
private:
const int numerator, denominator;
};
由于两次调用GCD函数,这会浪费时间。您还可以定义一个新的类成员,gcd_a_b
然后首先将gcd的输出分配给初始化程序列表中的输出,但这将导致内存浪费。
通常,有没有一种方法可以避免浪费的函数调用或内存?您能否在初始化列表中创建临时变量?谢谢。
5
您是否有证据表明“ GCD函数被调用了两次”?提到过两次,但这与发出两次调用它的代码的编译器不同。编译器可能会推断它是纯函数,并在第二次提及时重用其值。
—
埃里克塔
@EricTowers:是的,在某些情况下,编译器有时可以在实践中解决该问题。但前提是他们可以看到定义(或对象中的某些注释),否则无法证明它是纯净的。您应该在启用链接时优化的情况下进行编译,但并非所有人都可以。该函数可能在库中。或认为这是一个功能的情况下确实有副作用,并调用它恰好一次是正确的事情?
—
Peter Cordes
@EricTowers有趣的一点。实际上,我确实尝试通过在GCD函数中放置一个print语句来检查它,但是现在我意识到这将阻止它成为纯函数。
—
Qq0
@ Qq0:您可以通过查看编译器生成的asm进行检查,例如,将Godbolt编译器资源管理器与gcc或clang一起使用
—
Peter Cordes
-O3
。但是对于任何简单的测试实现,它实际上都可以内联函数调用。如果__attribute__((const))
在原型上使用或pure而不提供可见的定义,则应让GCC或clang在两个调用之间使用相同的arg进行公共子表达式消除(CSE)。请注意,Drew的答案甚至适用于非纯函数,因此它要好得多,您应该在函数无法内联时使用它。
通常,最好避免使用非静态const成员变量。const并不经常应用的少数几个领域之一。例如,您不能分配类对象。您可以将emplace_back转换为向量,但前提是容量限制不会影响调整大小。
—
doug