目录
- 1 问题
- 2 引用初始化的说明
- 3 示例代码
1 问题
函数的引用参数要求比较严格,今天就因为一个问题卡住了。我将此问题简化为下面的演示代码,
#include <stdio.h>
class CBase
{
public:
CBase() {};
virtual ~CBase() {};
};
class CDerive :public CBase
{
public:
CDerive() {};
~CDerive() {};
};
void TestPoiterRef(CBase*& pBase)
{
printf("\nThis a simple fun!");
}
int main()
{
CDerive* pDerive = new CDerive;
TestPoiterRef(pDerive); //这一步编译出错
delete pDerive;
}
这段代码编译过程的错误提示如下:
最开始我非常奇怪,CDerive明明是CBase的子类啊?为什么CDerive* 无法转化为 CBase*&?
后来,经过研究之后,发现自己提出的这个问题错的离谱。
2 引用初始化的说明
以下摘自cpp官方文档,建议大家直接查阅该链接:https://en.cppreference.com/w/cpp/language/reference_initialization#Lifetime_of_a_temporary
以下中文翻译由自己完成,仅用于备忘,建议大家直接阅读上面英文版的。
一个对象T的引用可以被对象T、返回值为T的函数、或者可以隐式转为T的对象初始化。一旦初始化,引用就不可指向其他对象。
以下情况初始化引用:
1) 初始化左值引用时
2) 初始化右值引用时
3)在函数调用表达式中,当函数参数具有引用类型时
4) 在 return 语句中,当函数返回引用类型时
5) 使用成员初始值设定项初始化引用类型的非静态数据成员时
引用初始化的副作用为:
- 如果初始化程序是一个大括号的初始化列表 ( { arg1, arg2, … } 或者 {.des1 = arg1 , .des2 { arg2 } … }(自C++20)起),遵循列表初始化规则。
- 否则,如果是左值引用
- 如果target是一个左值表达式,并且它的类型是T或者派生自T,并且和ref相比具有同样或者较少的CV限定符(const 或者 volatile )时,那么引用ref将被绑定到这个左值或者左值的子对象。
- 否则,如果target不是类型T或者派生自T,但是target有一个转换函数,可以转为类型T或者派生自类型T的一个左值,具有同样或者较少的CV限定符,那么引用会被绑定到转换函数返回的左值对象(或者左值对象的子对象)。
- 如果target是一个左值表达式,并且它的类型是T或者派生自T,并且和ref相比具有同样或者较少的CV限定符(const 或者 volatile )时,那么引用ref将被绑定到这个左值或者左值的子对象。
- 否则,如果是一个const类型左值引用或者右值引用
- 如果 target 是非位字段 rvalue 或函数 lvalue,并且其类型为 T 或派生自 T,同样或更少符合 cv 限定,则引用将绑定到初始值设定项表达式的值或其基子对象(如有必要,在具体化临时之后)(自 C++17 起)。
- 如果target是一个左值表达式,并且它的类型是T或者派生自T,并且和ref相比具有同样或者较少的CV限定符(const 或者 volatile )时,那么引用ref将被绑定到这个左值或者左值的基类对象。
- 否则,如果targe的类型不是T或派生自 T,但是targe具有转换为右值或T类型(或者从T派生的)左值的函数,具有同样或更少 cv 限定,则引用绑定到转换函数的结果或其基类子对象(如有必要,在具体化临时之后)(自 C++17 以来)。
- 否则,目标将隐式转换为 T。引用绑定到转换的结果(这是个临时变量,即使它是标量类型,也会保留 T 的 cv 限定)(自 C++17 起)。如果target(或者,用户定义的转换函数转换后的结果)是 T 类型或从 T 派生的,则它必须具有与 T 相同或更少的 cv 限定符,并且,如果引用是右值引用,则不得是左值(自 C++11 起)。
这就是引用变量初始化的规则,现在回到前文自己犯的错误,为什么错了呢?很简单,函数原型为void TestPoiterRef(CBase*& pBase),引用参数是CBase*的引用(请注意,不是CBase的引用,而是CBase*的引用,一个指针的引用),因此,当我们将CDerive*类型的实参传递给TestPoiterRef时,编译器发现CDerive*这个类型无法隐式转为CBase*,所以报错了!
3 示例代码
这是官网的一个DEMO,挺全面的。
#include <sstream>
#include <utility>
struct S
{
int mi;
const std::pair<int, int>& mp; // reference member
};
void foo(int) {}
struct A {};
struct B : A
{
int n;
operator int&() { return n; }
};
B bar() { return B(); }
//int& bad_r; // error: no initializer
extern int& ext_r; // OK
int main()
{
// Lvalues
int n = 1;
int& r1 = n; // lvalue reference to the object n
const int& cr(n); // reference can be more cv-qualified
volatile int& cv{n}; // any initializer syntax can be used
int& r2 = r1; // another lvalue reference to the object n
// int& bad = cr; // error: less cv-qualified
int& r3 = const_cast<int&>(cr); // const_cast is needed
void (&rf)(int) = foo; // lvalue reference to function
int ar[3];
int (&ra)[3] = ar; // lvalue reference to array
B b;
A& base_ref = b; // reference to base subobject
int& converted_ref = b; // reference to the result of a conversion
// Rvalues
// int& bad = 1; // error: cannot bind lvalue ref to rvalue
const int& cref = 1; // bound to rvalue
int&& rref = 1; // bound to rvalue
const A& cref2 = bar(); // reference to A subobject of B temporary
A&& rref2 = bar(); // same
int&& xref = static_cast<int&&>(n); // bind directly to n
// int&& copy_ref = n; // error: can't bind to an lvalue
double&& copy_ref = n; // bind to an rvalue temporary with value 1.0
// Restrictions on temporary lifetimes
// std::ostream& buf_ref = std::ostringstream() << 'a';
// the ostringstream temporary was bound to the left operand
// of operator<< but its lifetime ended at the semicolon so
// the buf_ref is a dangling reference
S a {1, {2, 3}}; // temporary pair {2, 3} bound to the reference member
// a.mp and its lifetime is extended to match
// the lifetime of object a
S* p = new S{1, {2, 3}}; // temporary pair {2, 3} bound to the reference
// member p->mp, but its lifetime ended at the semicolon
// p->mp is a dangling reference
delete p;
// Imitate [[maybe_unused]] applied to the following variables:
[](...){}
(
cv, r2, r3, rf, ra, base_ref, converted_ref,
a, cref, rref, cref2, rref2, copy_ref, xref
);
}
C++的权威语法说明文档为:
https://en.cppreference.com/w/Main_Page