【发布时间】:2020-12-28 22:57:51
【问题描述】:
class T {
size_t *pData; // Memory allocated in the constructor
friend T operator+(const T& a, const T& b);
};
T operator+(const T& a, const T& b){ // Op 1
T c; // malloc()
*c.pData = *a.pData + *b.pData;
return c;
}
T do_something(){
/* Implementation details */
return T_Obj;
}
具有动态内存的简单class T。考虑
T a,b,c;
c = a + b; // Case 1
c = a + do_something(b); // Case 2
c = do_something(a) + b; // Case 3
c = do_something(a) + do_something(b); // Case 4
- 案例 1 使用 1 个 malloc()
- 案例 2 使用 2 malloc()
- 案例 3 使用 2 个 malloc()
- 案例 4 使用 3 malloc()
我们可以通过额外定义做得更好,
T& operator+(const T& a, T&& b){ // Op 2
// no malloc() steeling data from b rvalue
*b.pData = *a.pData + *b.pData;
return b;
}
案例 2 现在只使用了 1 个 malloc(),但是案例 3 呢?我们需要定义 Op 3 吗?
T& operator+(T&& a, const T& b){ // Op 3
// no malloc() steeling data from a rvalue
*b.pData = *a.pData + *b.pData;
return b;
}
此外,如果我们确实定义了 Op 2 和 Op 3,鉴于右值引用可以绑定到左值引用这一事实,编译器现在有两个同样合理的函数定义可以在案例 4 中调用
T& operator+(const T& a, T&& b); // Op 2 rvalue binding to a
T& operator+(T&& a, const T& b); // Op 3 rvalue binding to b
编译器会抱怨函数调用不明确,定义 Op 4 是否有助于解决编译器不明确的函数调用问题?因为我们没有通过 Op 4 获得额外的性能
T& operator+(T&& a, T&& b){ // Op 4
// no malloc() can steel data from a or b rvalue
*b.pData = *a.pData + *b.pData;
return b;
}
对于 Op 1、Op 2、Op 3 和 Op 4,我们有
- 案例 1:1 malloc(调用了 Op 1)
- 案例 2:1 malloc(调用了 Op 2)
- 案例 3:1 malloc(调用了 Op 3)
- 案例 4:1 malloc(调用了 Op 4)
如果我的理解是正确的,我们将需要每个运算符四个函数签名。这在某种程度上似乎并不正确,因为每个操作员都有很多样板和代码重复。我错过了什么吗?有没有一种优雅的方式来实现同样的目标?
【问题讨论】:
-
您的
operator+与+的典型期望有点不一致。通常operator+作用于两个二进制操作数并返回一个 new 对象而不改变两个输入。在这种情况下修改输入对于大多数 C++ 开发人员来说都是不可取和意料之外的。这种低效率已经出现在标准类型中,例如std::string'soperator+ -
@Eljay 你是对的
const T&&没有意义。我已经进行了编辑 -
@Human-Compiler 请注意,我只是重用右值。在这种情况下,修改输入并不是意外或不可取的,因为右值无论如何都会很快被销毁。重用右值可以节省移动(或者在最糟糕的情况下甚至是副本)。根据
T的大小和+在循环中执行的次数,它很快就会变得很重要。 -
大多数矩阵库使用lazy operation(也有其优点/缺点)。
标签: c++ c++11 operator-overloading move-semantics generic-programming