将左值绑定到右值引用--g++错误
Binding lvalue to rvalue reference -- g++ bug?
作为另一个问题的答案,我想发布以下代码(也就是说,我想基于这个想法发布代码):
#include <iostream>
#include <utility> // std::is_same, std::enable_if
using namespace std;
template< class Type >
struct Boxed
{
Type value;
template< class Arg >
Boxed(
Arg const& v,
typename enable_if< is_same< Type, Arg >::value, Arg >::type* = 0
)
: value( v )
{
wcout << "Generic!" << endl;
}
Boxed( Type&& v ): value( move( v ) )
{
wcout << "Rvalue!" << endl;
}
};
void function( Boxed< int > v ) {}
int main()
{
int i = 5;
function( i ); //<- this is acceptable
char c = 'a';
function( c ); //<- I would NOT like this to compile
}
然而,当MSVC 11.0在最后一次调用时阻塞时,正如IHMO应该的那样,MinGW g++4.7.1只是接受它,并使用右值引用形式参数调用构造函数。
在我看来,它看起来就好像左值绑定到右值引用。一个油嘴滑舌的答案可能是将左值转换为右值。但问题是,这是一个编译器错误吗;不,神圣的标准怎么允许这样做?
EDIT:我设法将其全部简化为以下非常简短的示例:
void foo( double&& ) {}
int main()
{
char ch = '!';
foo( ch );
}
用MSVC 11.0编译失败,用MinGW 4.7.1编译对吗?
我还没有检查规范,但我想char
可以自动转换为int
。由于不能分配任何内容(它是r值),将传递类型为int
的临时变量的r值(更明确地说,是(int)c
值)。
我发现N3290(与C++11标准相同)包含将double&&
绑定到由int
左值生成的右值的非规范性示例,以及§8.5.3中的更新措辞
“如果T1是与T2相关的参考,并且该参考是右值参考,初始值设定项表达式不应为左值”
据报道,这些规则旨在避免低效的额外复制。尽管我看不出这样的复制是如何不能被优化掉的。不管怎样,理由是否合理–当然,这并不是一个合理的效果–允许使用以下代码,并使用MSVC 11和MinGW g++4.7:进行编译
struct Foo {};
struct Bar { Bar( Foo ) {} };
void ugh( Bar&& ) {}
int main()
{
Foo o;
ugh( o );
}
显然MSVC 11不允许左值->右值转换是错误的。
编辑:我了解到有关于此问题的缺陷报告,DR 1414。2012年2月的结论是,当前的行为规范是“正确";,大概是关于它在多大程度上反映了意图。然而,据报道,委员会仍在讨论这一问题,大概是关于意图的实用性。
您认为这是有效的吗?
void foo( double ) {} // pass-by-value
int main()
{
char ch = '!';
foo( ch );
}
有一个从char
到double
的隐式转换,所以这个函数是可行的。
在你编辑的问题中的例子也是一样,有一个隐式转换产生了一个临时的(即右值),右值引用参数绑定到该临时。如果你喜欢,你可以明确转换:
void foo( double&& ) {} // pass-by-reference
int main()
{
char ch = '!';
foo( double(ch) );
}
但在这种情况下,这并没有真正改变任何事情。如果double
->char
只能显式转换(例如,对于具有显式构造函数或显式转换运算符的类类型),但double
到char
是有效的隐式转换,则这是必要的。
你想到的"右值引用不能绑定到左值"规则指的是将T&&
绑定到T
左值,并且该规则没有被破坏,因为double&&
没有绑定到CCD16,而是绑定到由隐式转换创建的临时。
该规则的存在不仅是为了防止不必要的额外复制,而且是为了解决以前规则中存在的真正安全问题,请参阅http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2008/n2812.html
编辑:有人问委员会反射器上的这种行为是否可取(见DR 1414),决定是的,这种行为是有意的,也是正确的。用于达到这一立场的论点之一是,使用当前规则,此代码更高效:
std::vector<std::string> v;
v.push_back("text");
根据当前规则,通过隐式转换创建临时std::string
,然后调用std::vector<T>::push_back(T&&)
,并将临时CCD_18移动到向量中。如果push_back
过载对于转换的结果不可行,那么上面的代码将调用std::vector<T>::push_back(const T&)
,这将导致复制。当前的规则使这个真实世界的用例更加高效。如果规则说右值引用不能绑定到隐式转换的结果,那么你必须更改上面的代码才能获得移动的效率:
v.push_back( std::string{"text"} );
IMHO当构造函数不是显式的时,必须显式地构造std::string
是没有意义的。我希望显式/隐式构造函数的行为一致,并且希望第一个push_back
示例更高效。
- 将对象数组的引用传递给函数
- 什么时候在C++中返回常量引用是个好主意
- 我想将一个对T类型的非常量左值引用绑定到一个T类型的临时值
- 何时在引用或唯一指针上使用移动语义
- 如何在c++中使用引用实现类似python的行为
- 编译C++时未定义的引用
- Ctypes wstring通过引用传递
- c++r值引用应用于函数指针
- 理解c++中的引用
- C++取消引用指针.为什么会发生变化
- 如何修复此错误:未定义对"距离(浮点数,浮点数,浮点数,浮点数,浮点数)"的引用
- 我的项目不会像"undefined reference to `grpc::g_core_codegen_interface'"那样使用未定义的引用错误进行编译
- C++Boost Asio Pool线程,带有lambda函数和传递引用变量
- 强制转换为引用类型
- 引用一个已擦除类型(void*)的指针
- 向量元素的引用地址与它所指向的向量元素的地址不同.为什么
- 具有默认值的引用获取函数
- 如何使用基类指针引用派生类成员
- 使用取消引用的指针的多态性会产生意外的结果.为什么?
- 如何引用基类的派生类?