重载运算符:const vs非const返回类型:性能的任何差异?

时间:2012-08-12 02:14:17

标签: c++ performance optimization operator-overloading

如果我们去关于C ++运算符的维基百科article,我们举个例子:

Addition : a + b -> T T::operator +(const T& b) const;

因此运算符返回类型为T的非const 。 如果我们查看此guideline,作者会说返回类型应为 const ,以避免使用以下语法:

(a+b) = c

现在假设这个语法不会打扰我,并认为a和b是大型数组。从“纯”性能的角度来看,返回类型中缺少const关键字是否会阻止编译器的优化(g ++和intel icpc with -O3)?如果aswer是“是”,为什么?

3 个答案:

答案 0 :(得分:7)

这是一个有趣的问题。在C ++ 03中,没有更好的机会使用这两个选项中的任何一个进行优化,这将是一个样式选择的问题(我自己不相信const的整个回归以避免不可能的错误)。

另一方面,在C ++ 11中,它实际上可能会产生影响。特别是,如果您的类型支持移动操作,并且无法省略复制/移出返回的值,那么通过const返回,您实际上是禁用移动 *

// T is move assignable, with the usual declaration of a move assignment operator
T f();
const T g();
int main() {
   T t;
   t = f();     // can move
   t = g();     // cannot move!!!
}

在您的特定情况下,它取决于大数组对您来说意味着什么,如果它们是std::array(或者具有自动存储的数组),那么它们就无法移动,所以这无论如何都不是一个选项,但如果大数组是动态分配的内存,移动将比复制更有效。请注意,在C ++ 11中, presence 而不是const的缺失会导致性能下降。


* 如果移动赋值运算符将参数 rvalue-reference 带到const,则 100%为真那么它可以被移动。但是标准库中没有一个类型以这种方式接受参数,我也不希望人们这样做(在 move 操作中需要const_cast(构造函数/赋值)并且它没有任何意义:如果你计划移动(窃取)它,为什么你声称不修改它?

答案 1 :(得分:2)

由于您正在返回类型为T的临时rvalue实例,因此除非在赋值操作中存在一些全局副作用,否则此语句不会执行任何操作,例如对作为数据成员的静态变量的修改类型为T,输出到终端等等。因此,根据类型以及赋值运算符是否为编译器默认赋值运算符,可以在优化过程中安全地省略整个操作。如果类型为T的用户定义的赋值运算符,那么分配操作将不会被省略,但如前所述,除非存在全局副作用,否则在生命周期内不会执行任何操作 - 语句执行的时间,因为您没有将c的值存储在驻留在命名和可访问的内存位置的对象中。

请注意,如果您确实将返回T类型声明为const,并且您的运算符方法不是const类方法,则会禁用某些类型的运算符链接,以及许多其他有用的东西,比如调用有副作用的方法。例如:

(a+b).print(); //assuming print() is non-const method

或假设operator+不是const类方法,

d = (a+b) + c;

答案 2 :(得分:0)

我想说永远不要将operator +作为成员函数来实现。只需实现operator +=,这绝对不是常量。然后你提供了一个关注operator +的全局函数,我认为这是最常用的方法。

T& T::operator +=(const T& t)
{
    // add t to object ...
    return *this;
}

T operator +(T t1, const T& t2)
{
    t1 += t2
    return t1; // RVO will eliminate copy (or move constructor)
}