【问题标题】:Is it ok to return default argument's value by const reference?可以通过 const 引用返回默认参数的值吗?
【发布时间】:2020-02-28 00:09:39
【问题描述】:

是否可以通过 const 引用返回默认参数的值,如下例所示:

https://coliru.stacked-crooked.com/a/ff76e060a007723b

#include <string>

const std::string& foo(const std::string& s = std::string(""))
{
    return s;
}

int main()
{
    const std::string& s1 = foo();
    std::string s2 = foo();

    const std::string& s3 = foo("s");
    std::string s4 = foo("s");
}

【问题讨论】:

  • 简单测试:用你自己的类替换std::string,这样你就可以跟踪构造和破坏。
  • @user4581301 如果序列是正确的,它并不能证明构造是好的。
  • @user4581301 “当我尝试它时它似乎工作”是关于未定义行为的绝对最糟糕的事情
  • 应该指出,这个问题的措辞是一个误导性的花絮。您不是通过 const 引用返回默认参数的值,而是将 const 引用返回到 const 引用(... 到默认参数)。
  • @HerrJoebob 100% 同意该声明,但不同意您使用它的上下文。我阅读它的方式解决了“对象的生命周期何时结束?”弄清楚何时调用析构函数是一个很好的方法。对于 Automatic 变量,应该按时调用析构函数,否则您会遇到大问题。

标签: c++ language-lawyer object-lifetime default-arguments reference-binding


【解决方案1】:

在您的代码中,s1s3 都是悬空引用。 s2s4 都可以。

在第一次调用中,从默认参数创建的临时空std::string 对象将在包含调用的表达式的上下文中创建。因此,它将在s1 的定义结束时死亡,从而使s1 悬空。

在第二次调用中,临时的std::string对象被用来初始化s2,然后它就死掉了。

在第三次调用中,字符串文字 "s" 用于创建一个临时的 std::string 对象,该对象也在 s3 的定义结束时终止,留下 s3 悬空。

在第四次调用中,使用值为"s" 的临时std::string 对象来初始化s4,然后它就死掉了。

参见 C++17 [class.temporary]/6.1

在函数调用 (8.2.2) 中绑定到引用参数的临时对象一直存在,直到包含调用的完整表达式完成为止。

【讨论】:

  • 答案中有趣的部分是断言默认参数将在调用者的上下文中创建。纪尧姆的标准报价似乎支持这一点。
  • @Peter-ReinstateMonica See [expr.call]/4, “...每个参数的初始化和销毁​​都发生在调用函数的上下文中。...”
【解决方案2】:

not safe:

一般来说,临时的生命周期不能通过以下方式进一步延长 “传递它”:第二个引用,从对的引用初始化 临时绑定的,不影响其生命周期。

【讨论】:

  • 那么你认为std::string s2 = foo(); 有效吗(毕竟没有明确传递引用)?
  • @Peter-ReinstateMonica 说一个是安全的,因为新对象将被构建。我的回答仅仅是关于生命周期的扩展。其他两个答案已经涵盖了所有内容。我不会重复我的。
【解决方案3】:

这取决于你之后如何处理字符串。

如果您的问题是我的代码是否正确?那么是的。

来自[dcl.fct.default]/2

[例子:声明

void point(int = 3, int = 4);

声明一个可以用零、一或二调用的函数 int 类型的参数。可以通过以下任何方式调用它:

point(1,2);  point(1);  point();

最后两个调用相当于point(1,4)point(3,4), 分别。 — 结束示例]

所以你的代码实际上相当于:

const std::string& s1 = foo(std::string(""));
std::string s2 = foo(std::string(""));

您的所有代码都是正确的,但在任何这些情况下都没有引用生命周期延长,因为返回类型是引用。

由于您使用临时函数调用函数,因此返回字符串的生命周期不会延长语句。

const std::string& s1 = foo(std::string("")); // okay

s1; // not okay, s1 is dead. s1 is the temporary.

您使用s2 的示例没问题,因为您在状态结束之前从临时文件中复制(或移动)。 s3s1 有同样的问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-10-26
    • 1970-01-01
    • 1970-01-01
    • 2011-04-18
    • 2014-04-28
    • 2019-01-31
    • 2012-09-13
    相关资源
    最近更新 更多