将左值绑定到右值参考 - g ++ bug?

时间:2012-10-14 10:06:58

标签: c++ compiler-errors

作为对another question的回答,我想发布以下代码(也就是说,我想根据这个想法发布代码):

#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只接受它,并使用rvalue reference formal参数调用构造函数。

看起来像,好像左值被绑定到右值引用。一个滑稽的答案可能是左值转换为右值。但问题是,这是一个编译器错误,如果不是,那么神圣标准是如何允许的呢?


编辑:我设法将其全部缩减为以下非常简短的示例:

void foo( double&& ) {}

int main()
{
    char ch = '!';
    foo( ch );
}

无法使用MSVC 11.0编译,使用MinGW 4.7.1进行编译,这是对的吗?

3 个答案:

答案 0 :(得分:2)

我没有查看规范,但我猜char可以自动转换为int。由于您无法分配任何内容(它是r值),因此R值将传递给类型为int的临时变量(更明确地为(int)c值)。

答案 1 :(得分:2)

我发现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在不允许左值时是错误的 - &gt;右值转换。

<小时/> 编辑:我了解到有关此问题的缺陷报告DR 1414。 2012年2月的结论是,当前的行为规范是“正确的”,可能与它反映意图的程度有关。然而据报道,委员会仍在讨论这个问题,大概是关于意图的实用性。

答案 2 :(得分:0)

大概你同意这是有效的吗?

void foo( double ) {}  // pass-by-value

int main()
{
    char ch = '!';
    foo( ch );
}

chardouble的隐式转换,因此该功能可行。

在您编辑的问题的示例中,它是相同的,有一个隐式转换产生一个临时(即一个右值),而rvalue-reference参数绑定到该临时值。如果您愿意,可以明确转换:

void foo( double&& ) {}  // pass-by-reference

int main()
{
    char ch = '!';
    foo( double(ch) );
}

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

“rvalue-reference无法绑定到左值”规则,您正在考虑将T&&绑定到T左值,并且该规则不会被破坏,因为{{ 1}}不绑定到double&&,它绑定到由隐式转换创建的临时值。

该规则不仅用于防止不必要的额外复制,而且用于修复先前规则中存在的真正安全问题,请参阅http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2008/n2812.html

编辑:有人询问委员会反对者是否需要这种行为(请参阅DR 1414),并且确定是,这种行为是有意的并且是正确的。用于达到该位置的一个论点是,使用当前规则,此代码更有效:

char

使用当前规则,通过隐式转换创建临时std::vector<std::string> v; v.push_back("text"); ,然后调用std::string,并将临时移动到向量中。如果std::vector<T>::push_back(T&&)重载对转换结果不可行,那么上面的代码会调用push_back,这将导致副本。当前的规则使这个真实世界的用例更有效。如果规则说rvalue-refs无法绑定到隐式转换的结果,则必须更改上面的代码以获得移动的效率:

std::vector<T>::push_back(const T&)

恕我直言,当构造函数不明确时,必须显式构造v.push_back( std::string{"text"} ); 是没有意义的。我希望显式/隐式构造函数具有一致的行为,我希望第一个std::string示例更有效。