10

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

#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 只是接受它,并使用右值引用形式参数调用构造函数。

在我看来,左值好像绑定到右值引用。一个灵活的答案可能是将左值转换为右值。但问题是,这是否是编译器错误,如果不是,神圣标准如何允许这样做?


编辑:我设法将其简化为以下非常简短的示例:

void foo( double&& ) {}

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

使用 MSVC 11.0 编译失败,使用 MinGW 4.7.1 编译,这是对的吗?

4

3 回答 3

2

我没有检查规范,但我想char可以自动转换为int. 由于您不能分配任何东西(它是 r 值),因此将传递类型为临时变量int(更明确地表示值)的 R 值。(int)c

于 2012-10-14T10:16:40.123 回答
2

我发现 N3290(与 C++11 标准相同)包含绑定double&&到从左值生成的右值的非规范示例,以及第8.5.3 节int中的更新措辞

“如果 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 不允许 lvalue -> rvalue 转换是错误的。


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

于 2012-10-14T11:54:43.583 回答
0

大概你同意这是有效的?

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

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

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

在您编辑的问题中的示例中也是如此,有一个隐式转换会产生一个临时(即右值)并且右值引用参数绑定到该临时。如果您愿意,可以使该转换显式:

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

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

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

您正在考虑的“右值引用不能绑定到左值”规则是指将 a 绑定T&&T左值,并且该规则没有被破坏,因为double&&没有绑定到char,它绑定到由创建的临时隐式转换。

该规则的存在不仅是为了防止不必要的额外复制,而且是为了解决以前规则中存在的真正安全问题,请参阅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&&)调用该临时对象,并将该临时对象移入向量中。如果该push_back重载对于转换结果不可行,那么上面的代码将调用std::vector<T>::push_back(const T&)这将导致一个copy。当前的规则使这个现实世界的用例更加高效。如果规则说 rvalue-refs 不能绑定到隐式转换的结果,您将不得不更改上面的代码以获得移动的效率:

v.push_back( std::string{"text"} );

恕我直言,当构造函数不显式时,必须显式构造 a 是没有意义的std::string。我希望显式/隐式构造函数的行为一致,并且我希望第一个push_back示例更有效。

于 2012-10-14T15:45:17.433 回答