首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >普通对象传递的普通参数、引用参数和const引用参数之间的差异

普通对象传递的普通参数、引用参数和const引用参数之间的差异
EN

Stack Overflow用户
提问于 2022-01-10 11:28:18
回答 1查看 111关注 0票数 2

因为我是c++的初学者,所以有些问题不太明白。这个问题是在我读C++第五引物的时候偶然发现的。

我有一个包含3个不同构造函数(由Constructor 1、Constructor 2、Constructor 3命名)的Cat类:

代码语言:javascript
复制
class Cat {
    friend class Cats;

private:
    std::string name;

public:
    Cat() = default;

    // Constructor 1
    Cat(std::string n): name(n) {
        std::printf("Call: Cat(std::string n)\n");
    }

    // Constructor 2
    Cat(std::string& n): name(n) {
        std::printf("Call: Cat(std::string& n)\n");
    }

    // Constructor 3
    Cat(const std::string& n): name(n) {
        std::printf("Call: Cat(const std::string &n)\n");
    }

};

我想用两种不同的方式实例化这个类:

代码语言:javascript
复制
class C7T17_Main {
public:
    void run() {

        // Instantiation 1
        Cat cat(std::string("Cathy"));
       
        // Instantiation 2
        std::string s("Mike");
        Cat cat2(s);

    }
};

然后问题就来了:

  • 对于Constructor 1
代码语言:javascript
复制
- `Instantiation 1` and `Instantiation 2` both work well
  • 对于Constructor 2
代码语言:javascript
复制
- `Instantiation 1` raises an error. The compiler complains that _'Candidate constructor not viable: expects an lvalue for 1st argument'_
- `Instantiation 2` works normally.
  • 对于Constructor 3
代码语言:javascript
复制
- `Instantiation 1` and `Instantiation 2` both work well

我猜是:

  • Instantiation 1实际上并不创建变量,而是初始化cat的临时值,因此它不适合作为引用参数。
  • 对于constructor 3,const引用参数表示可以接受临时值进行初始化的常量。

期待你的帮助。:)

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-01-10 11:38:05

很简单:

  1. ctor 1通过复制接收参数(通过传递);
  2. ctor 2接收非const lvalue引用的参数,因此它只支持non lvalue
  3. ctor 3接收const值引用的参数,因此它支持非const值、const值和非const值。

实例化1实际上并不创建变量,而是初始化cat的临时值,因此它不适合作为引用参数。

是的,它创建了一个prvalue,它可以通过引用传递,如下所示:

代码语言:javascript
复制
// ctor 4
Cat( std::string&& n ) // see the double ampersand

ctor 4只接受引用的临时人员(r值)。

对于构造函数3,const引用参数表示一个常量,可以接受用于初始化的临时值。

是的,正如我前面提到的,它可以绑定到所有的值。

对于Constructor 2:实例化1引发一个错误。编译器抱怨“候选构造函数不可行:期望第一个参数的lvalue”

这是预期的行为。

重要注:

代码语言:javascript
复制
Cat(std::string&& n): name(n) { // here n is of type rvalue refernece to string
    std::printf("Call: Cat(std::string& n)\n");
}

具有rvalue引用类型的函数参数本身就是rvalue。因此,在上面的代码中,n是一个变量,它是一个lvalue。因此,它有一个身份,不能移动(您需要使用std::move,以使其可移动)。

额外注意事项:

您甚至可以将一个lvalue传递给一个只接受以下rvalue的函数:

代码语言:javascript
复制
Cat( std::string&& n );

std::string s("Mike");
Cat cat2( std::move(s) );

std::move执行简单的强制转换。它将lvalue转换为xvalue,这样它就可以被只接受rvalue的函数使用。

C++11中的值范畴

看一看这个:

对上述图像的解释

在C++11中,表达式:

  • 具有同一性且不能从其上移动的称为lvalue表达式;
  • 具有标识和可移出的称为xvalue表达式;
  • 没有标识和可以移动的是被称为prvalue (“纯rvalue")表达式;
  • 没有标识,也不能被移出不是used6。

具有标识的表达式称为"glvalue表达式“(glvalue表示”广义lvalue")。glvalue和xvalue都是glvalue表达式。

可以从中移动的表达式称为"rvalue表达式“。prvalues和rvalue都是rvalue表达式。

价值范畴上阅读有关此主题的更多信息。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/70651696

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档