问题标签 [rvalue-reference]
For questions regarding programming in ECMAScript (JavaScript/JS) and its various dialects/implementations (excluding ActionScript). Note JavaScript is NOT the same as Java! Please include all relevant tags on your question; e.g., [node.js], [jquery], [json], [reactjs], [angular], [ember.js], [vue.js], [typescript], [svelte], etc.
c++ - C++0x 右值引用——左值-右值绑定
这是C++0x 右值引用和临时的后续问题
在上一个问题中,我询问了这段代码应该如何工作:
由于隐式临时性,似乎应该调用移动重载,这发生在 GCC 而不是 MSVC(或 MSVC 的 Intellisense 中使用的 EDG 前端)。
这段代码呢?
看来,根据我之前的问题的答案,该功能g1
是合法的(并且被 GCC 4.3-4.5 接受,但不被 MSVC 接受)。但是,GCC 和 MSVC 都拒绝g2
,因为第 13.3.3.1.4/3 条禁止左值绑定到右值引用参数。我理解这背后的基本原理 - 在 N2831“解决右值引用的安全问题”中对此进行了解释。我还认为 GCC 可能正在按照该论文的作者的意图实施该条款,因为 GCC 的原始补丁是由其中一位作者(Doug Gregor)编写的。
但是,我不认为这是非常直观的。对我来说,(a) a在概念上比 aconst string &
更接近a ,并且(b)编译器可以在 中创建一个临时字符串,就好像它是这样写的:string &&
const char *
g2
实际上,有时复制构造函数被认为是隐式转换运算符。在语法上,这是由复制构造函数的形式提出的,标准甚至在第 13.3.3.1.2/4 节中特别提到了这一点,其中派生基转换的复制构造函数被赋予了比其他用户定义的更高的转换等级转换:
将类类型的表达式转换为相同的类类型被赋予精确匹配等级,而将类类型的表达式转换为该类型的基类被赋予转换等级,尽管事实上复制/移动为这些情况调用构造函数(即,用户定义的转换函数)。
(我假设这是在将派生类传递给类似 的函数时使用的,该函数void h(Base)
按值获取基类。)
动机
我提出这个问题的动机类似于如何在添加新的 c++0x 右值引用运算符重载时减少冗余代码(“如何在添加新的 c++0x 右值引用运算符重载时减少冗余代码”)中提出的问题。
如果你有一个函数接受许多可能移动的参数,并且如果可以的话会移动它们(例如工厂函数/构造函数:Object create_object(string, vector<string>, string)
或类似的),并且想要移动或复制每个参数,你很快就开始编写很多代码。
如果参数类型是可移动的,那么可以只编写一个按值接受参数的版本,如上所述。但是,如果参数是(传统)不可移动但可交换的类(如 C++03),并且您无法更改它们,那么编写右值引用重载会更有效。
因此,如果左值确实通过隐式副本绑定到右值,那么您可以只编写一个重载create_object(legacy_string &&, legacy_vector<legacy_string> &&, legacy_string &&)
,它或多或少会像提供右值/左值引用重载的所有组合一样工作——作为左值的实际参数将被复制然后绑定对于参数,作为右值的实际参数将直接绑定。
澄清/编辑:我意识到这实际上与按值接受可移动类型的参数相同,例如 C++0x std::string 和 std::vector (除了概念上调用移动构造函数的次数)。但是,对于可复制但不可移动的类型,它并不相同,包括所有具有显式定义的复制构造函数的 C++03 类。考虑这个例子:
如果g
调用f
,则x
和y
将被复制-我看不到编译器如何移动它们。如果f
改为声明为接受legacy_string &&
参数,它可以避免调用者std::move
在参数上显式调用的那些副本。我不明白这些是如何等效的。
问题
我的问题是:
- 这是对标准的有效解释吗?无论如何,这似乎不是传统的或有意的。
- 它有直觉意义吗?
- 这个想法有我没有看到的问题吗?似乎你可以在不完全预期的情况下悄悄地创建副本,但无论如何这就是 C++03 中的现状。而且,它会使一些重载当前不可行时可行,但我认为这在实践中不是问题。
- 这是一个足够显着的改进,值得为 GCC 制作一个实验性补丁吗?
c++ - 如何对可变参数函数中的所有参数调用 std::forward ?
我只是在编写一个通用对象工厂并使用 boost 预处理器元库来制作一个可变参数模板(使用 2010 并且它不支持它们)。我的函数使用 rval 引用并std::forward
进行完美转发,这让我开始思考……当 C++0X 出现并且我有一个标准编译器时,我会使用真正的可变参数模板来做到这一点。但是,我会如何呼吁std::forward
论点?
我能想到的唯一方法是需要手动解包 ...params ,我也还没到那里。有没有更快的语法可以工作?
c++ - 参考重载,而不是唯一的按值传递+ std::move?
似乎有关 C++0x 右值的主要建议是将移动构造函数和移动运算符添加到您的类中,直到编译器默认实现它们。
但是,如果您使用 VC10,等待是一种失败的策略,因为自动生成可能要到 VC10 SP1,或者在最坏的情况下,VC11 才会出现。很可能,对此的等待将以年为单位。
这就是我的问题。编写所有这些重复的代码并不好玩。而且看着很不爽。但对于那些被认为速度较慢的课程来说,这是一个很受欢迎的负担。对于数百甚至数千个小班来说,情况并非如此。
::sighs:: C++0x 应该让我写更少的代码,而不是更多!
然后我有了一个想法。我猜是很多人分享的。
为什么不通过价值传递一切?std::move + 复制省略不会使这几乎是最优的吗?
示例 1 - 典型的 Pre-0x 构造函数
缺点:右值的浪费副本。
示例 2 - 推荐的 C++0x?
优点:大概是最快的。
缺点:很多代码!
示例 3 - 按值传递 + std::move
优点:没有额外的代码。
缺点:在情况 1 和 2 中浪费了一步。如果SomeClass
没有移动构造函数,性能将受到很大影响。
你怎么看?这个对吗?与减少代码的好处相比,所发生的移动是否是普遍可以接受的损失?
c++ - 在右值方法中从 *this 移动?
在 C++11 中,可以根据表示调用方法的对象的表达式是左值还是右值来重载方法。如果我*this
从通过右值调用的方法返回,我是否需要显式地move
从*this
或不需要?
不幸的是,我不能简单地在我的编译器上测试它,因为 g++ 还不支持这个特性:(
c++ - 最小和完美转发
min 算法通常表示如下:
但是,这不允许使用 form 的构造min(a, b) = 0
。您可以通过额外的重载来实现:
我想做的是通过完美转发统一这两个重载:
但是,g++ 4.5.0 会发出警告,min(2, 4)
因为我返回了对临时的引用。我做错什么了吗?
好的,我明白了。问题在于条件运算符。在我的第一个解决方案中,如果我调用min(2, 4)
条件运算符会看到一个 xvalue,因此会从转发的对象中移动x
以生成一个临时对象。当然,通过引用返回它是危险的!如果我转发整个表达式而不是x
单独转发y
,编译器不再抱怨:
好的,我摆脱了算术类型的引用:)
c++ - 右值引用是否允许隐式转换?
下面的代码合法吗?
g++ 4.5.0 编译此代码没有任何问题。
c++ - 以一个临时的地址,扭曲
我有一个 function address_of
,它返回 a Pointer
(封装 a shared_ptr
)到它的参数。address_of
需要同时处理左值和右值,因此有两个版本address_of
:一个接受引用,另一个接受右值引用。由于获取临时地址是一件坏事™,因此右值版本address_of
需要执行移动构造才能Pointer
真正拥有某些东西。实现很简单:
并按预期获取临时作品的地址:
但是当我使用以下代码进行测试时:
GCC 抱怨调用address_of
不明确:
我的印象是一元*
总是返回一个左值,在这种情况下甚至不应该考虑右值版本。这里到底发生了什么?
c++ - 模板参数的引用变量的问题
以下小示例显示了我的问题:
错误信息(GCC):
错误:'static void X::xxx(T&&) [with T = int&]' 不能重载
错误:with 'static void X::xxx(T&) [with T = int&]'</p>
为什么?T = int&
--->T&
替换为int&&
in static void xxx(T& x)
?
如果问题的答案是肯定的,那么:
T&
不是左值引用,而是成为右值引用!- 以下代码应该可以工作:
但它没有:
错误信息(GCC):
错误:没有匹配函数调用 'X::xxx(int)'<br> 注意:候选者是:static void X::xxx(T&) [with T = int&]
然后T&
withT = int&
不等于T&&
并且不是右值引用。但如果不是,为什么第一个例子不起作用?(这是一个递归问题!)
但是指针类型没有出现类似的问题:
为什么引用在这种行为中有所不同?
c++ - 使用 std::forward 的主要目的是什么以及它解决了哪些问题?
在完美转发中,std::forward
用于将命名的右值引用转换为t1
未命名的t2
右值引用。这样做的目的是什么?inner
如果我们将t1
&作为左值,这将如何影响被调用的函数t2
?
c++ - 右值参考
在尝试从这里理解 Rvalue 引用时,我无法理解两件事
- 如果向量中有 N 个字符串,则每个副本可能需要多达 N+1 个内存分配,并且 [...]
'N+1' 中的 +1 是什么?
2.作者是如何突然得出下面的指导方针的
指南:不要复制你的函数参数。相反,按值传递它们并让编译器进行复制。
我错过了什么吗?