重载运算符:const和非const返回类型:是否有性能差异?

9

如果我们去维基百科上有关C ++运算符的文章,我们可以看到如下示例:

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

因此,操作符返回一个类型为T的非常量。如果我们查看这个指南,作者说返回类型应该是常量,以避免以下语法:

(a+b) = c

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

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中,存在而不是缺少const可能会导致性能下降。
* 这并非100%正确,如果移动赋值运算符通过对const的rvalue引用获取参数,则可以移动。但是标准库中的任何类型都不以这种方式获取参数,我也不希望人们这样做。(它需要在move操作(构造函数/赋值)中进行const_cast,并且这毫无意义:如果您计划从中“移动”(窃取),为什么要声明不修改它??)

2

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

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

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

假设 operator+ 不是一个 const 类方法,则:

d = (a+b) + c;

0
我认为不要将operator +实现为成员函数。只需实现operator +=,它肯定不是const。然后提供一个全局函数来处理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)
}

好的建议,但他询问的是返回值的常量性,而不是成员函数本身是否应该是const。对于常规函数来说,这意味着,“在编译器可以和不能优化的情况下,T operator+(const T &, const T &);const T operator+(const T &, const T&) 之间的性能差异是什么?” - James O'Doherty

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接