C++11 昂贵的右值临时
C++11 expensive rvalue temporary
本文关键字:C++11 更新时间:2023-10-16
我有一些轻物体可以推动和操纵,然后我喜欢将它们包含在更复杂的物体中。有一个查找表应该保持未修改。这个想法看起来很简单,但这样做的一行 - b += c(a);
- 创造了一个昂贵的临时。
#include <vector>
static int count;
struct costly {
/* std::map<std::string, int> and whatnot */
int b;
~costly() { count++; }
costly(int b): b(b) { }
costly &operator+= (costly &rhs) { b += rhs.b; return *this; }
};
/* Note the assumption above constructor exists for rhs */
costly operator* (const costly &lhs, costly rhs) {
rhs.b *= lhs.b; return rhs;
}
struct cheap {
/* Consider these private or generally unaccessible to 'costly' */
int index, mul;
cheap(int index, int mul): index(index), mul(mul) { }
costly operator() (const std::vector<costly> &rhs) {
/* Can we do without this? */
costly tmp = rhs[index] * mul; return tmp;
}
};
int main(int argc, char* argv[]) {
std::vector<costly> a = {1, 2}; costly b(1); cheap c = {1, 2};
/* Above init also calls the destructor, don't care for now */
count = 0;
b += c(a);
return count;
}
我一直在阅读 RVO 和 C++11 的 rvalues,但还不能很好地理解它们,以完全消除引入的中间值。上面只创建一个,因为 rhs 的构造函数可用。最初我有这个;
costly operator* (costly lhs, int rhs) {
lhs.b *= rhs; return lhs;
}
/* ... */
costly operator() (const std::vector<costly> &rhs) {
return rhs[index] * mul;
}
但是,与我的直觉相反,这导致count
甚至为2。为什么编译器不明白我的意图?
RVO 不适用于函数参数,因此您的*
运算符会禁止它。要启用 RVO,您需要参数的本地副本。然后,您可以通过提供采用右值引用的重载来优化(前提是costly
具有高效的移动复制结构)。例如
costly operator*(const costly& c, int i)
{
costly ret = c;
ret += 1;
return ret;
}
costly operator*(costly&& c, int i)
{
costly ret = std::move(c);
ret += 1;
return ret;
}
这是一种完全不同的方法,与是否通过 RVO 进行优化是正交的,但这里是:
既然你是内部数据成员,使复制变得昂贵或多或少是恒定的,为什么不直接避免复制该特定成员呢?
如果您像这样更改costly
:
struct costly {
shared_ptr<const map<string, int>> lookup_table;
int m;
...
};
复制变得便宜得多。请注意,指向表的指针是非常量,即使它指向的映射是常量。
Sean Parent对此进行了很好的演讲,关于他们如何在Photoshop中实现历史和图层。到目前为止,我无法查找URL,因为我的带宽有限。
我认为部分
问题在于算术运算符最适合复制相对便宜的值类型。如果你想完全避免复制costly
我认为最好避免这些运算符过载。
它可能给costly
太多的逻辑,但你可以添加一个昂贵的函数,它完全可以做你想要的,而无需制作任何副本:
void addWithMultiple(const costly& rhs, int mul) {
b += rhs.b * mul;
}
然后可以通过cheap
调用,如下所示:
void operator() (costly &b, const std::vector<costly> &a) {
b.addWithMultiple(a[index], mul);
}
但是,从您开始的内容来看,这是一个相当大的重构,因此可能无法满足您的所有需求。
相关文章:
- MSVC是否支持C++11样式的属性而不是__declspec
- 创建LinkedList退出,返回代码为-11(SIGSEGV)
- 我可以将一个用clang c++11编译的对象与另一个用c++17编译的对象链接起来吗
- 继承:构造函数,初始化C++11中基类的类C数组成员
- 如何将模板转换为C++11之前的模板
- c++11评估顺序(未定义的行为)
- C++中的VLA,扩展名为std=C++11
- 代码在我的计算机上运行良好,但是在将其提交给coursera时遇到未知的信号11问题
- "类模板示例<int>;"语句对 C++11 是什么意思?
- this_thread::sleep_for和计时时钟之间的关系是否由C++11标准指定
- 如何使用lock_guard在c++11中实现scoped_lock功能
- C++11 中不同类型的对象的 std::array 的替代方案
- 为什么 -mmacosx-version-min=10.10 不阻止使用标记为从 10.11 开始的函数?
- 为什么我的C++代码中出现'Segmentation Fault: 11'行?
- 强枚举类型定义:Clang Bug 还是 C++11 标准不确定性?
- 别名模板的专业化 C++11 中没有开销的最佳替代方案
- STLPort using C++11
- Qt 5.11.2 (Clang 8.0 (Apple), 64 位), 找不到 QJSEngine 文件
- 在 C++11 中,如何查找并返回以给定字符串开头的字符串向量中的所有项?
- C++11 迭代向量的新方法?