为什么一个指向字符串参数的常量引用可以接受字符串字面值?

11

为什么对字符串参数的const引用可以接受字符串字面值?如"hello"一样的字符串字面值不是变量,为什么这段代码是有效的?

class CVector {
    public:
        int x, y;
        CVector() {};
        ~CVector() { delete ptr; }
        string* ptr;
        void doSomething(const string& str) { ptr = new string(str); }
        void print() { cout << "\n" << *ptr; }
};
int main()
{
    result.doSomething("asdas");
    result.print();
    return 0;
}
首先,我认为将引用作为参数是为了避免拷贝过程并直接访问作为参数传递的变量(尽管我可能仍然正确)。但字符串文字"asdas"不是一个变量,那么为什么参数可以使用字符串文字作为参数?我的意思是,由于参数str是一个引用,它会成为该实体的别名,对吗?如果是这样,那么字面量是否变成了变量?
参数列表是否应该包括string& str而不是const引用,这样字面量就会用于构造str?
常量引用不是会使引用的实体在引用存在时一直存活吗?如果是这样,那么你为什么要对一个字面量这样做?

简单来说,有一个转换(即ctor),它接受字符串字面量并创建字符串实例。 - Kobi
3个回答

11

当你做的时候

result.doSomething("asdas");
编译器查找是否有doSomething(const char[]);,但找不到。由于没有合适的函数,它会尝试查找一个可从const char[]构造的类型的重载项,并找到了doSomething(const string& str)。由于编译器可以进行一次用户定义的转换,它将从字符串字面量构造一个临时的std::string,通过引用传递给该函数并使用const引用。

参数列表中难道不应该是string& str而不是const reference吗,这样字面值就可以在构造str时使用了吗?

不是的,只有使用对const的引用才能正常工作,使用普通引用是不行的,因为普通引用无法绑定到临时对象上。

一个const引用不是会让所引用的实体在引用存在的时间内保持存活吗?如果是这样,为什么要将其用于字面值呢?

对const引用的引用仅会延长函数局部对象的生命周期。在函数范围内,对象将一直存在,因为调用该函数的表达式还未结束,但如果您尝试在类中保留对std::string的引用,则无法工作。

实际上,您的代码被转换成:

int main()
{
    CVector result
    {
        std::string temp = "asdas";
        result.doSomething(temp);
    }
    result.print();
    return 0;
}

请注意,传递到函数中的对象是构造的临时“string”对象,而不是字面本身。 - Mark Ransom
谢谢。但是参数const string&str)是对字面值“asdas”本身的引用还是创建自该字面值的另一个字符串的引用? - user7982333
所以编译器实际上在每次编写字符串字面值时都会创建一个字符串?就像在这种情况下,编译器从字符串字面值创建一个字符串,然后使参数引用该字符串。 - user7982333
1
@JakeBlandon 它的生命周期持续到表达式结束。也就是说,在 doSomething 返回之后并在调用 print 之前,它将被销毁。 - NathanOliver
1
@JakeBlandon 它们的作用域限定在创建它们的完整表达式中。当表达式结束时,临时变量将被销毁。通常,完整表达式是以 ; 结尾的内容。至于更多资源,我没有其他的了,除了这里的书籍/参考资料:https://dev59.com/_3RC5IYBdhLWcg3wK9yV - NathanOliver
显示剩余5条评论

4

字符串字面值,例如"hello",不是变量。

"变量"这个术语定义得相当模糊,并没有任何具体的概念来支持它。

表达式"hello"表示一个静态存储期对象,您无法修改它。与任何其他表达式一样,它可能被用于初始化其他对象。在这种情况下,您正在使用表达式(在其衰减为const char*后)初始化一个std::string

您所错过的是“中间步骤”,即从该文字中构建一个临时std::string,其生命周期通过绑定到const引用来延长。

所以,大致上:

const std::string temp{"Hello world"};   // the compiler creates this transparently
const std::string& ref = temp;  // this is yours, and it extends temp's life

阅读有关隐式转换的更多信息。


1

std::string有一个隐式的const char *转换构造函数。

编译器允许进行一次隐式转换以使类型匹配,因此它使用该构造函数将const char *转换为std::string临时对象,从而顺利完成,因为const&(const左值引用)允许绑定到临时对象(并延长其生命周期)。


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