【发布时间】:2020-07-16 21:42:43
【问题描述】:
如果我有两个不同的常量成员变量,它们都需要基于同一个函数调用进行初始化,有没有办法在不调用函数两次的情况下做到这一点?
例如,分子和分母为常数的分数类。
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 的输出分配给初始化列表中的那个,但这会导致内存浪费。
一般来说,有没有办法在不浪费函数调用或内存的情况下做到这一点?您能否在初始化列表中创建临时变量?
【问题讨论】:
-
你有证据证明“GCD 函数被调用了两次”吗?它被提到了两次,但这与编译器发出的代码调用它两次不同。编译器可能会推断它是一个纯函数,并在第二次提及时重用它的值。
-
@EricTowers:是的,在某些情况下,编译器有时可以在实践中解决这个问题。但前提是他们可以看到定义(或对象中的某些注释),否则无法证明它是纯的。您应该在启用链接时优化的情况下进行编译,但不是每个人都这样做。该函数可能在库中。或者考虑确实有副作用的函数的情况,并且只调用一次是正确的问题?
-
@EricTowers 有趣的一点。我确实试图通过在 GCD 函数中放置一个 print 语句来检查它,但现在我意识到这会阻止它成为一个纯函数。
-
@Qq0:您可以通过查看编译器生成的 asm 来检查,例如将 the Godbolt compiler explorer 与 gcc 或 clang
-O3一起使用。但可能对于任何简单的测试实现,它实际上都会内联函数调用。如果您在原型上使用__attribute__((const))或 pure 而不提供可见定义,它应该让 GCC 或 clang 在具有相同 arg 的两个调用之间进行公共子表达式消除 (CSE)。请注意,Drew 的答案甚至适用于非纯函数,因此它要好得多,您应该在 func 可能不内联的任何时候使用它。 -
一般来说,最好避免使用非静态 const 成员变量。 const一切不经常适用的少数几个领域之一。例如,您不能分配类对象。您可以将 emplace_back 放入向量中,但前提是容量限制不会影响大小调整。
标签: c++ oop constructor constants initializer-list