【问题标题】:Why passing an lvalue as an rvalue argument fails?为什么将左值作为右值参数传递失败?
【发布时间】:2019-01-30 11:29:18
【问题描述】:

将左值引用作为右值引用参数传递不会编译。编译器可以使用其复制构造函数创建一个临时对象并将其作为右值传递,但事实并非如此。不过,如果类型不匹配,它确实会调用构造函数。

我很感兴趣,为什么会这样? C++ 标准中的哪种设计逻辑迫使编译器以不同的方式处理复制构造函数?

void do_smth(string && s)
{}

void f(const char * s)
{
  do_smth(s); // passes a temporary string constructed from const char*
}

void g(const string & s)
{
  do_smth(s); // does not compile
}

void h(const string & s)
{
  do_smth(string(s)); // ok again
}

不想实现第二个签名do_smth(const string &)怎么办?我应该改用传递值void do_smth(string s) 吗?

假定对象string 具有移动构造函数,按值void do_smth(string s) 和右值引用void do_smth(string && s) 之间还有哪些其他区别?

【问题讨论】:

  • 是的,按值传递在这里会非常有用。
  • 相关question.

标签: c++ rvalue-reference


【解决方案1】:

这是右值引用的重点。它们绑定到右值绑定到左值

当您同时具有左值版本和右值版本时,这就是您确保调用所需重载的方式。例如,复制构造函数与移动构造函数。

这是一个功能。

另外,你的左值是const,它与do_smth 的签名不匹配,即使它没有使用右值引用。

如果您希望do_smth 能够采用任何一种 类型的表达式,要么将其设为const string&,要么将其设为模板并使其采用@987654321 @ T&&(看起来像一个右值引用,但又不是)。

如果您希望do_smth 存储它自己的字符串版本,那么让它取一个值:如果需要,您仍然可以通过在调用站点使用std::move 来避免复制(或通过传递一个右值,这将触发字符串自己的移动构造函数)。

由于右值引用绑定规则是如何制定的,看看所有选项如何可用和优雅?这一切都融合在一起。

决定让do_smth 采取什么完全取决于它要“做什么”,只有你可以说那是什么。

【讨论】:

  • 你是什么意思“当你有一个左值版本和一个右值版本时,确保你想要的重载被调用”?如果我也制作了void do_smth(const char* s) 签名,编译器会在 f() 中调用它,而不是构造一个临时对象,不是吗?为什么左值和右值重载在这种方式上是不同的,所以当没有左值版本的签名时,编译器不会调用复制构造函数来自动将左值转换为右值?
  • @RomanMaltsev 抱歉,我不明白您指的是什么“差异”。是的,如果您将左值 const char* 传递给接受 const char* 的函数,那将起作用。这就是按值传递的情况。您还可以将其传递给接受 const char*& 的函数(即左值引用)。您无法将其传递给接受 const char*&&(即右值引用)的函数。
  • 我的意思是,为什么string(const char*) 构造函数是“好的”,可以将 const char* 静默转换为临时字符串并将其作为右值传递。但是 `string(const string &) 对于相同的静默转换来说是“坏”构造函数?
  • @RomanMaltsev 从技术上讲,原因是因为允许进行一次隐式转换。 std::string 具有来自 const char* 的隐式(即非显式)构造函数,但复制构造绝不是隐式转换。不过,您的问题是否有意义是有效的。
  • @MaxLanghof 您是什么意思“复制构造绝不是隐式转换”?复制构造函数可以用显式关键字标记或不标记。标记后,它还可以防止按值传递的自动复制,因此即使使用void do_smth(string s) 签名而不是右值签名,g 中的代码也不会编译。因此,当复制参数以按值传递时,复制构造函数被隐式调用,就像转换构造函数一样。因此,如果是关于显式关键字,则复制构造函数应该对字符串和字符串&& 以相同的方式工作或不工作。
【解决方案2】:

h 中,您提供 const 字符串的 non const 副本,因此 constg 案例

当然,void do_smth(string s){...} 会在调用时生成副本,并且 constnon const string 都可以在参数中给出

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-03-08
    • 2018-12-13
    • 2016-04-11
    • 2020-09-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多