Const引用默认值 (注:此处为一个提问标题,无需回答)

24

可能是重复问题:
如何初始化默认值为类的函数参数

#include <string>

void foo1(const std::string& s = std::string());

void foo2(std::string& s = std::string());

void foo3(const std::string s = std::string());

void foo4(std::string s = std::string());

error at foo2(): default argument for ‘std::string& s’ has type ‘std::string {aka std::basic_string<char>}’

我明白编译器的意思,但是我不明白为什么这个错误不适用于foo1()


2
你可以使用= {}代替= std::string()来节省空间。 - chris
1
即使是老旧的= ""也可以工作,并且需要输入更少的内容。 - David Rodríguez - dribeas
我认为这些答案有点混淆(但也很有启发性)。简单的回答是,你的本质上是正确的。引用在某种意义上应该像指针一样工作。显然,你不能指向一个临时变量。通常情况下,你会指向内存中存储感兴趣值的某个位置。为了方便起见,const引用被“扩展”以能够指向临时变量。 - AturSams
1
本质上,接受非const引用的函数的整个目的是修改它所引用的内容(否则它将是一个const引用),因此当您想要为非const引用设置默认值时,这对您来说应该没有任何意义。您可能需要重载一个不接受任何输入的函数版本。 - AturSams
1
换句话说,当我们不指定const时,我们关心值存储的位置(地址)以及该值本身(有时更多)。使用const引用时,我们通常根本不关心值存储在哪里。 - AturSams
3个回答

27

像foo2一样,你不能获取一个对临时对象的非const引用。

请注意,这不仅限于默认参数。对于函数变量,你将获得相同的错误: http://ideone.com/g7Tf7L

#include <string>
using std::string;

#include <iostream>
using std::cout; using std::endl;

int main()
{
    string s1        = string("s1"); // OK, copy it
    const string& s2 = string("s2"); // OK, const reference to it
    string& s3       = string("s3"); // ERROR! non-const reference not allowed!

    cout
            << s1 << ", "
            << s2 << ", "
            << s3 << endl;
    return 0;
}

当你取一个常量引用指向一个临时变量时,这个临时变量的生命周期会被扩展到引用的生命周期(§12.2,引用自我拥有的C++11草案n3337):

有两种情况下,临时变量的销毁时间不同于完整表达式的结束时间。

...

第二种情况是当一个引用绑定到一个临时变量时。引用绑定的临时变量或是绑定到该引用子对象的完整对象将在引用的生命周期内保留,除了:

  • 在构造函数的构造函数初始化器(12.6.2)中绑定到引用成员的临时变量将一直持续到构造函数退出。
  • 在函数调用中绑定到引用参数的临时变量(5.2.2)将一直持续到包含该调用的完整表达式完成。
  • 绑定到函数返回语句中返回值的临时变量(6.6.3)的生命周期不会被扩展;该临时变量将在返回语句中完整表达式的结束时被销毁。
  • 在新初始化器(5.3.4)中绑定到引用的临时变量将一直持续到包含新初始化器的完整表达式完成。

  1. 你能详细说明一下(或者指引我去哪里),为什么不行,但是可以使用const引用吗?
  2. 这个临时变量的生命周期是多久?
- aiao
考虑 void myAbs(int &j) { if (j < 0) j = -j; } short q; ... myAbs(q);。如果允许的话,这会创建一个临时的int,将其设置为绝对值,然后销毁它,从而不更改q。糟糕。使用 const 声明向编译器承诺您不会这样做。 - David Schwartz
6
在C++11中,如果使用右值引用,则可以对临时对象进行非const引用。而此示例中的foo2()则使用了左值引用 - Remy Lebeau
当从另一个动态库中调用foo1函数时,它能正常工作吗?我听说我的同事遇到了这个问题,但无法确认。 - jaques-sam

6

也许会让你感到惊讶,但是你可以将临时表达式的值绑定到一个常量引用上,并且表达式的生命周期将扩展到该引用的生命周期。但是你不能使用非常量(左值)引用来做这件事。


foo3()foo4() 中,这个临时变量的生存期是多长时间? - aiao
1
@aiao 在所有情况下,生命周期都是调用完整表达式评估时间。引用函数参数不会延长参数的生命周期,也不会缩短它。 - Johannes Schaub - litb
2
@aiao:临时变量的生命周期与其所处的完整表达式的生命周期相同。foo1foo3 实际上是一样的,但是如果 foo3 中,临时变量仅用于初始化变量s,即复制初始化。 - Kerrek SB
更准确地说,只有本地const引用可以延长临时对象的生命周期,但也有例外。类const引用的初始化并不会如此做。 - dragonxlwang

1

foo3foo4的声明是合法的,因为这些函数的参数不是引用。

foo2的声明是非法的,因为你不能将非const引用绑定到临时对象。

那么为什么foo1的声明是合法的呢?正是这个非常重要的const限定符使得这个声明合法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接