C ++ 0x右值引用和临时 [英] C++0x rvalue references and temporaries

查看:149
本文介绍了C ++ 0x右值引用和临时的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

(我问这个问题在comp.std.c ++的变化,但没有得到答案。)



为什么调用 f(arg)在这段代码中调用 $ ltc $ c> f ?

的const ref重载

  void f(const std :: string&); // less efficient 
void f(std :: string&&); //更高效

void g(const char * arg)
{
f(arg);
}



我的直觉认为 f(string& &)重载应该被选择,因为 arg 需要被转换为临时的,无论什么,并且临时匹配rvalue引用



这不是发生在gcc和 MSVC(编辑:感谢Sumant:它不会发生在GCC 4.3- 4.5)。在至少 G ++和 MSVC中,任何左值都不绑定到右值引用参数,即使有一个中间临时创建。实际上,如果不存在const ref过载,编译器诊断错误。但是,写 f(arg + 0) f(std :: string(arg)) 按照您的期望选择右值引用重载。



从我对C ++ 0x标准的读法看,它似乎隐式转换一个const char *考虑到如果 f(string&&&)是可行的,就像在传递一个const lvalue ref参数时一样。第13.3节(重载分辨率)在太多地方没有区分rvalue refs和const引用。此外,似乎防止lvalue绑定到右值引用(13.3.3.1.4 / 3)的规则不应该适用,如果有一个中间临时 - 毕竟,从临时移动是完全安全的。



是这样的:


  1. 我误读/误解标准,其中实现的行为是行为,并且有一个很好的理由为什么我的示例应该按照它的方式行事?

  2. 编译器供应商不知怎么做的错误?还是一个基于共同实现策略的错误?或者在例如

  3. 标准中的缺陷,或意外的后果,或者应该澄清的事情,这些都是由其他供应商复制的? ? questions / 2749263 / c0x-rvalue-references-lvalues-rvalue-binding> http://stackoverflow.com/questions/2749263/c0x-rvalue-references-lvalues-rvalue-binding

    解决方案

    GCC根据FCD做错了。 FCD在 8.5.3 上说关于引用绑定




    • 如果引用是lvalue引用和初始化器表达式是一个[lvalue /类类型] ...

    • 否则,引用应该是对非易失性const类型的左值引用(即cv1 const),或者引用是一个右值引用,初始化表达式应该是一个右值或一个函数类型。



    对于 std :: string&& 的调用不匹配,因为初始化器是一个左值。它不到达创建临时右值的位置,因为该子对象已经需要右值。



    现在,重载解析并不直接使用引用绑定来查看是否存在隐式转换序列。相反,它表示在 13.3.3.1.4 / 2


    的引用类型不直接绑定到参数表达式,转换序列是根据13.3.3.1将参数表达式转换为引用的基础类型所需的转换序列。


    因此,重载解决方案显示出胜利者,即使胜利者实际上不能绑定到该参数。例如:

      struct B {B(int){/ * ... * /}}; 
    struct A {int bits:1; };

    void f(int&);
    void f(B);
    int main(){A a; f(a.bits); }

    8.5 的参考绑定禁用位字段绑定到lvalue引用。但是重载分辨率表示转换序列是转换为 int 的转换序列,因此即使在稍后进行调用时,调用也不成立。因此我的位域例子是错误的。如果选择 B 版本,它会成功,但需要一个用户定义的转换。



    ,该规则存在两个例外。这些是


    除了隐式对象参数(参见13.3.1),如果需要绑定一个标准转换序列


    因此,以下调用是有效的:

    / p>

      struct B {B(int){/ * ... * /}}; 
    struct A {int bits:1; };

    void f(int&); / *绑定一个左值引用非常量到右值! * /
    void f(B);
    int main(){A a; f(1); }

    因此,您的示例调用 const T& version

      void f(const std :: string&); 
    void f(std :: string&&); //会绑定到lvalue!

    void g(const char * arg){f(arg); }

    但是,如果你说 f(arg + 0),你创建一个右值,因此第二个函数是可行的。


    (I asked a variation of this question on comp.std.c++ but didn't get an answer.)

    Why does the call to f(arg) in this code call the const ref overload of f?

    void f(const std::string &); //less efficient
    void f(std::string &&); //more efficient
    
    void g(const char * arg)
    {
         f(arg);
    }
    

    My intuition says that the f(string &&) overload should be chosen, because arg needs to be converted to a temporary no matter what, and the temporary matches the rvalue reference better than the lvalue reference.

    This is not what happens in GCC and MSVC (edit: Thanks Sumant: it doesn't happen in GCC 4.3-4.5). In at least G++ and MSVC, any lvalue does not bind to an rvalue reference argument, even if there is an intermediate temporary created. Indeed, if the const ref overload isn't present, the compilers diagnose an error. However, writing f(arg + 0) or f(std::string(arg)) does choose the rvalue reference overload as you would expect.

    From my reading of the C++0x standard, it seems like the implicit conversion of a const char * to a string should be considered when considering if f(string &&) is viable, just as when passing a const lvalue ref arguments. Section 13.3 (overload resolution) doesn't differentiate between rvalue refs and const references in too many places. Also, it seems that the rule that prevents lvalues from binding to rvalue references (13.3.3.1.4/3) shouldn't apply if there's an intermediate temporary - after all, it's perfectly safe to move from the temporary.

    Is this:

    1. Me misreading/misunderstand the standard, where the implemented behavior is the intended behavior, and there's some good reason why my example should behave the way it does?
    2. A mistake that the compiler vendors have somehow all made? Or a mistake based on common implementation strategies? Or a mistake in e.g. GCC (where this lvalue/rvalue reference binding rule was first implemented), that was copied by other vendors?
    3. A defect in the standard, or an unintended consequence, or something that should be clarified?

    EDIT: I have a follow-on question that is related: http://stackoverflow.com/questions/2749263/c0x-rvalue-references-lvalues-rvalue-binding

    解决方案

    GCC is doing it wrong according the FCD. The FCD says at 8.5.3 about reference binding

    • If the reference is an lvalue reference and the initializer expression is an [lvalue / class type] ...
    • Otherwise, the reference shall be an lvalue reference to a non-volatile const type (i.e., cv1 shall be const), or the reference shall be an rvalue reference and the initializer expression shall be an rvalue or have a function type.

    Your case for the call to the std::string && matches none of them, because the initializer is an lvalue. It doesn't get to the place to create a temporary rvalue, because that toplevel bullet already requires an rvalue.

    Now, overload resolution doesn't directly use reference binding to see whether there exist an implicit conversion sequence. Instead, it says at 13.3.3.1.4/2

    When a parameter of reference type is not bound directly to an argument expression, the conversion sequence is the one required to convert the argument expression to the underlying type of the reference according to 13.3.3.1.

    Thus, overload resolution figures out a winner, even though that winner may actually not be able to bind to that argument. For example:

    struct B { B(int) { /* ... */ } };
    struct A { int bits: 1; };
    
    void f(int&);
    void f(B);
    int main() { A a; f(a.bits); }
    

    Reference binding at 8.5 forbids bitfields to bind to lvalue references. But overload resolution says that the conversion sequence is the one converting to int, thus succeeding even though when the call is made later, the call is ill-formed. Thus my bitfields example is ill-formed. If it was to choose the B version, it would have succeeded, but needed a user defined conversion.

    However, there exist two exceptions for that rule. These are

    Except for an implicit object parameter, for which see 13.3.1, a standard conversion sequence cannot be formed if it requires binding an lvalue reference to non-const to an rvalue or binding an rvalue reference to an lvalue.

    Thus, the following call is valid:

    struct B { B(int) { /* ... */ } };
    struct A { int bits: 1; };
    
    void f(int&); /* binding an lvalue ref to non-const to rvalue! */
    void f(B);
    int main() { A a; f(1); }
    

    And thus, your example calls the const T& version

    void f(const std::string &);
    void f(std::string &&); // would bind to lvalue!
    
    void g(const char * arg) { f(arg); }
    

    However, if you say f(arg + 0), you create an rvalue, and thus the second function is viable.

    这篇关于C ++ 0x右值引用和临时的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆