普通对象和临时创建的对象传递的普通参数、引用参数和const引用参数的区别

Difference between ordinary parameter, reference parameter and const reference parameter passed by ordinary object and object created temporary

由于本人是c++初学者,有些问题不是很懂。这个问题是我在阅读 C++ primer 5th 时偶然遇到的。


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

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");
    }

};

我想以两种不同的方式实例化 class:

class C7T17_Main {
public:
    void run() {

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

    }
};

那么问题来了:


我的猜测是:


期待您的帮助。 :)

很简单:

  1. ctor 1 通过复制接收参数(通过 value);
  2. ctor 2通过非常量左值引用接收参数,所以它只支持非常量左值.
  3. ctor 3 通过 const 左值引用 接收参数,因此它支持 const-lvalue非常量左值非常量右值非常量右值.

Instantiation 1 does not actually create a variable, but a temporary value for initializing cat, so it is not suitable as a reference parameter.

是的,它创建了一个prvalue,并且可以像这样通过引用传递:

// ctor 4
Cat( std::string&& n ) // see the double ampersand

ctor 4 仅通过引用接受临时值 (rvalues)。

For constructor 3, the const reference parameter represents a constant that can accept a temporary value for initialization

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

For Constructor 2: Instantiation 1 raises an error. The compiler complains that 'Candidate constructor not viable: expects an lvalue for 1st argument'

这是预期的行为。

重要提示:

Cat(std::string&& n): name(n) { // here n is of type rvalue refernece to string
    std::printf("Call: Cat(std::string& n)\n");
}

右值引用类型的函数参数本身就是左值。所以在上面的代码中,n 是一个变量,它是一个左值。因此它有一个身份并且不能从移动(你需要使用std::move才能使它移动)。

补充说明:

您甚至可以像这样将左值传递给只接受右值的函数:

Cat( std::string&& n );

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

std::move 执行简单转换。它将 lvalue 转换为 xvalue,因此它可以被仅接受 rvalue 的函数使用。

C++11 中的值类别

看看这个:

上图的解释

In C++11, expressions that:

  • have identity and cannot be moved from are called lvalue expressions;
  • have identity and can be moved from are called xvalue expressions;
  • do not have identity and can be moved from are called prvalue ("pure rvalue") expressions;
  • do not have identity and cannot be moved from are not used[6].

The expressions that have identity are called "glvalue expressions" (glvalue stands for "generalized lvalue"). Both lvalues and xvalues are glvalue expressions.

The expressions that can be moved from are called "rvalue expressions". Both prvalues and xvalues are rvalue expressions.

value categories 阅读有关此主题的更多信息。