右值引用 && 的另一个优化。只需重命名字段
Posted
技术标签:
【中文标题】右值引用 && 的另一个优化。只需重命名字段【英文标题】:Yet another optimization with rvalue reference &&. Simply rename fields 【发布时间】:2016-12-08 18:43:39 【问题描述】:让我们考虑下一段代码:
struct Channel; // somewhere declared
using iter_t = std::set<Channel>::iterator;
std::set<Channel> myset;
std::pair<iter_t, bool> emplaced = myset.emplace(arg1,arg2);
然后emplaced.first
包含元素的迭代器,emplaced.second
表示元素是否已添加或已存在。 first 和 second 对我来说还不清楚。我想重命名这些字段:
struct ChannelAddedResult
iter_t channelIter;
bool wasAdded;
public: // --- Functions, constructors ---
ChannelAddedResult(std::pair<iter_t, bool> &&pa)
this->channel = pa.first;
this->added = pa.second;
;
此构造函数复制值。右值引用没有任何好处。对吧?
但是如何将std::pair<iter_t, bool>
转换为ChannelAddedResult
?这种类型是等价的。所以C
风格的对话可能看起来像:
union CarUni
std::pair<iter_t, bool> pair;
ChannelAddedResult car;
// Use
CarUni u;
u.pair = myset.emplace(arg1,arg2);
auto ch = *u.car.channelIter;
bool a = u.car.wasAdded;
这允许在没有额外副本的情况下实现重命名。可能是 C
-casting (ChannelAddedResult)emplaced_pair
会做同样的工作,但它在 C++ 中已被弃用。从类型安全的角度来看,这两种转换是危险的。
这种转换有 C++11 方式吗?
【问题讨论】:
您所说的“C 风格对话”在哪里?这就是C++。 C 风格的转换(在您的示例中 未使用)在 C++ 中不被弃用。总体而言,您的代码看起来不错,并且绝不“从类型安全的角度来看是危险的”。您到底关心什么?std::pair<iter_t, bool> &emplaced == myset.emplace(arg1,arg2);
应该是std::pair<iter_t, bool> &emplaced = myset.emplace(arg1,arg2);
std::pair<iter_t, bool> &emplaced == myset.emplace(arg1,arg2);
请避免发布因与您的问题无关的原因而无法编译的代码。我猜有两个错别字,但是......如果有错别字,那么您的代码中有多少其他随机功能是您不打算的错别字?
第二,你认为复制一个标准集迭代器和一个布尔值有多贵?您认为优化有多难?
昂贵 - 这里 2 份不计。但这只是sn-p。在一些更扩展的代码中,即放置 100000000000 个通道可能会减慢应用程序的速度。这个例子实现了重命名字段的想法。我可以想象其他的实现和变化。
【参考方案1】:
如果你不想复制,请移动:
ChannelAddedResult(std::pair<iter_t, bool> &&pa)
: channel(std::move(pa.first))
, added(std::move(pa.second))
虽然像bool
s 和迭代器这样的类型并没有真正的区别。
【讨论】:
【参考方案2】:我编写了一个小示例程序来展示我如何尝试一下。
我从您的问题中得到的是,您想将 first
和 second
与您的自定义类型名称(即 channelIter
和 wasAdded
)重命名。
我提取了你的示例代码并用它制作了一个更简单的程序。
#include <set>
#include <iostream>
struct Channel
int _a; // Declaring 2 simple args
int _b;
Channel(int a, int b) : _a(a), _b(b)
// Need to provide a < operator for custom datatype
bool operator<(Channel const & rhs) const
return (this->_a < rhs._a) || (this->_b < rhs._b);
;
using iter_t = std::set<Channel>::iterator;
struct ChannelAddedResult
iter_t _channelIter;
bool _wasAdded;
ChannelAddedResult(std::pair<iter_t, bool> && pa)
: _channelIter(pa.first), _wasAdded(pa.second)
;
int main()
std::set<Channel> myset;
auto u = ChannelAddedResult(myset.emplace(5, 6));
auto ch = *u._channelIter; // Access pair.first
bool a = u._wasAdded; // Access pair.second
std::cout << "ch._a => [" << ch._a << "] ch._b => [" << ch._b << "]\n";
return 0;
使用 -std=c++11 编译
$ g++ -std=c++11 set_sample.cpp -o set_sample
输出
$ ./set_sample
ch._a => [5] ch._b => [6]
这里要理解的是,ChannelAddedResult(std::pair<iter_t, bool> && pa)
是一个右值引用,应该传递一个右值才能有效地使用。所以它应该像ChannelAddedResult(myset.emplace(5, 6))
一样使用。这里myset.emplace(5,6)
是一个右值。所以不会创建副本。
相反,如果使用如下,则在两者之间创建了一个无用的副本。将左值传递给右值引用会破坏整个目的。
auto val = myset.emplace(5,6);
auto u = ChannelAddedResult(val); // val is an lvalue
上述代码无法编译。要使其正常工作,您需要将签名升级为 -
ChannelAddedResult(std::pair<iter_t, bool> const & pa)
: _channelIter(pa.first), _wasAdded(pa.second)
现在它可以同时使用右值和左值。
由于我们已经将std::pair
的数据复制到其他变量中,因此右值引用的使用不会对性能增加太多。在这种情况下,最好使用T const & pa
而不是T && pa
。
【讨论】:
对。感谢您提供完整的程序。我试图使我的帖子尽可能短。但据我了解,&&pa
没有任何计量,因为它的工作原理与const&pa
@kyb :您的理解不正确。 T && pa
是一个右值引用,将直接使用myset.emplace(5, 6)
的值(因为它在我的程序中是一个右值)。相反,使用T const & pa
而不是T && pa
会为其创建一个临时副本,使用它的临时副本,然后再销毁临时副本。
我已经编辑了我的答案以添加更多细节来解决您的疑问。再过一遍。以上是关于右值引用 && 的另一个优化。只需重命名字段的主要内容,如果未能解决你的问题,请参考以下文章