普通对象和临时创建的对象传递的普通参数、引用参数和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);
}
};
那么问题来了:
对于Constructor 1
:
Instantiation 1
和 Instantiation 2
都很好
对于Constructor 2
:
Instantiation 1
引发错误。编译器抱怨 'Candidate constructor not viable: expects an lvalue for 1st argument'
Instantiation 2
正常工作。
对于Constructor 3
:
Instantiation 1
和 Instantiation 2
都很好
我的猜测是:
Instantiation 1
实际上并没有创建变量,只是初始化cat的一个临时值,所以不适合作为参考参数。
对于constructor 3
,const引用参数表示一个常量,可以接受一个临时值进行初始化
期待您的帮助。 :)
很简单:
- ctor 1 通过复制接收参数(通过 value);
- ctor 2通过非常量左值引用接收参数,所以它只支持非常量左值.
- 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 阅读有关此主题的更多信息。
由于本人是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);
}
};
那么问题来了:
对于
Constructor 1
:Instantiation 1
和Instantiation 2
都很好
对于
Constructor 2
:Instantiation 1
引发错误。编译器抱怨 'Candidate constructor not viable: expects an lvalue for 1st argument'Instantiation 2
正常工作。
对于
Constructor 3
:Instantiation 1
和Instantiation 2
都很好
我的猜测是:
Instantiation 1
实际上并没有创建变量,只是初始化cat的一个临时值,所以不适合作为参考参数。对于
constructor 3
,const引用参数表示一个常量,可以接受一个临时值进行初始化
期待您的帮助。 :)
很简单:
- ctor 1 通过复制接收参数(通过 value);
- ctor 2通过非常量左值引用接收参数,所以它只支持非常量左值.
- 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 阅读有关此主题的更多信息。