将左值绑定到右值引用--g++错误

Binding lvalue to rvalue reference -- g++ bug?

本文关键字:引用 --g++ 错误 绑定      更新时间:2023-10-16

作为另一个问题的答案,我想发布以下代码(也就是说,我想基于这个想法发布代码):

#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 );
}

有一个从chardouble的隐式转换,所以这个函数是可行的。

在你编辑的问题中的例子也是一样,有一个隐式转换产生了一个临时的(即右值),右值引用参数绑定到该临时。如果你喜欢,你可以明确转换:

void foo( double&& ) {}  // pass-by-reference
int main()
{
char ch = '!';
foo( double(ch) );
}

但在这种情况下,这并没有真正改变任何事情。如果double->char只能显式转换(例如,对于具有显式构造函数或显式转换运算符的类类型),但doublechar是有效的隐式转换,则这是必要的。

你想到的"右值引用不能绑定到左值"规则指的是将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示例更高效。