【问题标题】:Implicitly generated function overloads for rvalue argument?为右值参数隐式生成函数重载?
【发布时间】:2020-05-18 04:17:52
【问题描述】:

如果之前有人问过这个问题,或者我遗漏了一些琐碎的事情,我很抱歉,但是以下参考资料对我来说并不清楚。我理解为什么在函数参数不是引用时允许传递右值而不是左值,但是我不明白当函数参数是 const 引用时允许传递右值而不是左值的原因(这对我来说没有意义)但被禁止传递通常的引用时(逻辑行为)。

假设我有以下代码

struct A
{
    explicit A(std::string s) : name{ s } { };
    A(const A& a) : name{ a.name } {  }
    A& operator=(const A& a) { name = a.name; return *this; }
    A(A&& a) noexcept : name{} { std::swap(name, a.name); }
    A& operator= (A&& a) noexcept { std::swap(name, a.name); return *this; }
    void talk() const{ std::cout << name << " says blablabla.\n"; }
private:
    std::string name;
};

void f(A a) {}
void g(A& a) {}
void h(const A& a) {}

int main()
{
    A a{ "a" };
    f(a);
    f(A{ "temp" });
    g(a);
    g(A{ "temp" }); // Compile error
    h(a);
    h(A{ "temp" });
}

现在,我明白了为什么 A&& 为 f 隐式生成重载,但是 A& 和 const A& 的行为让我感到困惑。

  1. 为什么编译器禁止传递 A&& 而不是 A& 而允许传递 A&& 而不是 A&?
  2. 这是错误还是功能?
  3. 如果它是一项功能,那么允许它的原因是什么?

谢谢。

编辑: 建议的问题不完全是我问的。我的问题是,为什么它允许绑定临时对象,即使它是 const。链接的问题询问“为什么它不是 const 时禁止”。

已接受的答案:eerorika 在 cmets 中对他的问题提供的答案最有意义,这是与 pre c++11 的向后兼容性。

【问题讨论】:

  • 请注意,该行正在尝试将临时绑定到非常量引用。
  • 我不确定。我的问题是为什么“它允许绑定”问题是“为什么禁止”。
  • 所以你对h()不是g()感到困惑?
  • 你可以这样说。我很好奇,为什么允许隐式绑定到临时值,无论它是否是 const 。两者都有潜在的危险。当然,您可以争辩说,除了读取它的值(例如,如果我使用 const 引用并在 setter 函数中获取它的地址)之外,在函数内部使用 const 引用做一些事情是一个坏主意,无论如何都应该不鼓励,但仍然如此。 .. 如果你确实允许这个绑定,为什么只在 const 引用的情况下才允许它。

标签: c++ overloading pass-by-reference


【解决方案1】:

这是一种将昂贵的值传递给函数的方法。它允许绑定到临时对象,因为有时函数的结果也很昂贵,并且您不想通过将每个昂贵的值保存为变量来污染代码...... C++ 为您做到了,这就是我们获得临时对象的方式。

它增加了一致性:

//  Whichever you pick, the code below shall be fine.
//using Value = int;
using Value = std::string;
//using Value = std::vector<std::string>;

Value get_something();
Value process(const Value & text);
void set_something(const Value & name);

set_something(process(get_something()); 

记住:在过去,没有移动语义。即使有了它,每次将值传递给函数时移动值也不适合我。

【讨论】:

  • 我对移动语义没有任何问题。我只是看不到接受 const 引用以接受 const rvalue 的隐式重载函数可以实现您提到的内容。
  • 它允许在不损失性能的情况下链接函数“set_something(process(get_something()))”。你还打算怎么做? “隐式重载”是什么意思?
  • 嗯,您必须显式删除函数的 && 版本以防止我描述的行为这一事实意味着存在过载?无论如何,没有性能优势,只是向后能力,因为接受 const A& 作为参数的函数无论如何都会使用复制语义和移动语义。要获得性能优势,您应该重载并显式编写 && 或使用 std::forward。
  • 接受“const A &”的函数不需要复制或移动,它可以通过传递的引用访问原始值。除了通过参考之外没有任何成本。
  • 您提供的示例 set_something(process(get_something()) 实际上意味着存在意味着复制或移动的分配。
【解决方案2】:

我不明白为什么当函数参数是 const 引用时允许[传递右值而不是左值]

这使得使用复制构造函数从右值复制成为可能。可能还有其他原因,但这是一个非常有用的功能。

这是错误还是功能?

在语言中有意指定。

【讨论】:

  • 但是如果我传递一个 const 引用,我不想复制它。我想传递一个参考,同时保护它不被修改。构造函数已经(不幸地)是一个特例,除了节省输入几行代码之外还有其他好处吗?
  • @RazielMagius 如果您不想复制某些内容,请不要调用复制的函数。尽管复制构造函数很特别,但它们越不特别越好。不需要不必要的专业。可能会有更多的好处,但我不会费心去想更多。增加的简单性和可读性对我来说已经足够了。
  • 我可能遗漏了一些东西,但是除了从编写显式移动构造函数或移动赋值运算符到“使从右值复制成为可能”的几行之外,它还提供了哪些其他好处?你是说事实并非如此,我每次都必须编写复制构造函数和移动构造函数?还是别的什么?
  • 用 h(const A& a) 调用函数不会复制,我也不会复制它。但它只是引入了潜在的内存泄漏。
  • @RazielMagius except a few lines from writing explicit move constructor or move assignment operators to "make copying from rvalues possible" 你必须记住,复制甚至在语言有移动构造函数之前就已经存在。在 C++11 之前,这不会使复制右值成为可能。并且在 C++11 之后删除使用复制构造函数复制右值的可能性将是向后不兼容的更改,没有任何好处。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多