C++: Const Reference
整理自 Thinking in C++
1. Seriously, there is NO const reference.
我们按照 const pointer 那一套的逻辑来嘛,大家讲道理嘛……
class T {};
int main() {
T t1, t2;
const T ct;
T* pt = &t1; // pt: pointer to T
const T* pct = &ct; // pct: pointer to const T
T* const cpt = &t2; // cpt: const pointer to T
T& rt = t1; // rt: reference to T
const T& rct = ct; // rct: reference to const T
T& const crt = t2; // ERROR. 'const' qualifiers cannot be applied to 'T&'
// Ops! So there is NO const reference!
}
但是好像到处都在用 “const reference”。更有甚者,编译器报错会用 “non-const reference” 这个词,比如 “invalid initialization of non-const reference of type ‘X&’ from an rvalue of type ‘X’“。其实说的都是 reference to const,你明白就好。
但是我决定不妥协!在自己的文章里坚持不用 const reference 这个词!(其实我也有队友,比如写这篇 [C++]寧以pass-by-reference-to-const取代pass-by-value 的台湾朋友)
2. 理解 reference 与 object 的强绑定关系
#include <iostream>
using namespace std;
class T {
public:
int i;
void modify();
T(int i);
};
T::T(int i) {
this->i = i;
}
void T::modify() {
i++;
}
int main() {
T t1(1), t2(2);
T& rt = t1;
rt = t2; // 等价于 t1 = t2;并不是将 rt 绑定到 t2,rt 仍然绑定 t1
// 这个时候 rt 绑定 t1;t1 == t2
cout << t1.i << endl; // 2
cout << t2.i << endl; // 2
cout << rt.i << endl; // 2
// rt 修改,t1 跟着变化,不影响 t2
rt.modify();
cout << t1.i << endl; // 3
cout << t2.i << endl; // 2
cout << rt.i << endl; // 3
// t1 修改,rt 跟着变化,还是不影响 t2
t1.modify();
cout << t1.i << endl; // 4
cout << t2.i << endl; // 2
cout << rt.i << endl; // 4
}
- reference 在 declare 的时候是要求直接初始化的(
T& rt = t1;
),这一初始化就直接绑定 object 了,之后你再用=
赋值(rt = t2;
)也改变不了这个绑定关系,而是直接等价于 object 之间的赋值(i.e. 等价于t1 = t2;
)。 - 所以 reference 和 pointer 在行为上有本质的不同:pointer 可以到处乱改到处乱指;而 reference 一旦绑定 object 之后就直接等价于 object 了
如果我们接受了 “reference 等价于 object” 的话,那么就可以认为foo(T t)
和foo(T& rt)
其实是接收相同的参数类型,只有 pass-by-value 和 pass-by-reference 的区别- 同理,研究
const T
的行为也就等价于研究cont T&
的行为
另外还看到过一个奇怪的言论,说 T& const crt = t2;
报错是因为 “reference 天生是 const”。这里 const 指的就是这种强绑定关系。不过鉴于 const reference 已经够歧义了,再说 “reference 天生是 const” 完全就是在添乱。这里必须表扬一下 wikipedia - const (computer programming),用 “redundant” 这个词就合适得多:
A declaration of a
const
reference is redundant since references can never be made to refer to another object.
int & const constRef = i; // Error the "const" is redundant
-> ~~~~~~~~~~ 2015-04-02 更新 ~~~~~~~~~~ <-
11 章开头给了一个描述我觉得写得很好:
References are like constant pointers that are automatically dereferenced by the compiler.
-> ~~~~~~~~~~ 2015-04-02 更新结束 ~~~~~~~~~~ <-
3. 大实验
class T {};
int main() {
T t;
const T ct;
T& rt = t;
const T& rct = ct;
/***** 注意以下 4 个 CASE 不能同时执行 *****/
/***** 每次只能执行一个 *****/
/***** CASE 1 *****/
t = ct; // OK
rt = ct; // OK
// object = const-object 可以,reference = const-object 自然也可以
/***** CASE 2 *****/
t = rct; // OK
rt = rct; // OK
// object = reference-to-const 可以,reference = reference-to-const 自然也可以
/***** CASE 3 *****/
ct = t; // ERROE. passing 'const T' as 'this' argument of 'T& T::operator=(const T&)' discards qualifiers
rct = t; // ERROE. passing 'const T' as 'this' argument of 'T& T::operator=(const T&)' discards qualifiers
// const-object = object 不行,reference-to-const = object 自然也不行
/***** CASE 4 *****/
ct = rt; // ERROE. passing 'const T' as 'this' argument of 'T& T::operator=(const T&)' discards qualifiers
rct = rt; // ERROE. passing 'const T' as 'this' argument of 'T& T::operator=(const T&)' discards qualifiers
// const-object = reference 不行,reference-to-const = reference 自然也不行
}
- 不能把
T
或者T&
赋值给一个const T
或者const T&
- 反过来把
const T
或者const T&
赋值给一个T
或者T&
是可以的 - 这一点和 C++: Const Pointer 是相反的
- 反过来把
class T { };
void foo(T& rt) { /* do nothing */ }
void bar(const T& rct) { /* do nothing */ }
void baz(T t) { /* do nothing */ }
void qux(const T t) { /* do nothing */ }
int main() {
T t;
const T ct;
T& rt = t;
const T& rct = ct;
foo(t); // OK
foo(rt); // OK
foo(ct); // ERROR. invalid initialization of reference of type 'T&' from expression of type 'const T'
foo(rct); // ERROR. invalid initialization of reference of type 'T&' from expression of type 'const T'
bar(t); // OK
bar(rt); // OK
bar(ct); // OK
bar(rct); // OK
baz(t); // OK
baz(rt); // OK
baz(ct); // OK
baz(rct); // OK
qux(t); // OK
qux(rt); // OK
qux(ct); // OK
qux(rct); // OK
}
- 不能把
const T
或者const T&
实参传给一个T&
形参- 除此之外没有其他禁忌
- 这一点和 C++: Const Pointer 是类似的
- 试验结果有点出乎我意料,因为
foo(T& rt)
和baz(T t)
并不只有 pass-by-value vs. pass-by-reference 这一个区别
class T {
public:
int i;
void modify();
T(int i);
};
T::T(int i) {
this->i = i;
}
void T::modify() {
i++;
}
int main() {
T t(1);
const T ct(3);
const T& rct1 = ct;
const T& rct2 = t;
t.modify(); // OK
ct.modify(); // ERROR. passing 'const T' as 'this' argument of 'void T::modify()' discards qualifiers
rct1.modify(); // ERROR. passing 'const T' as 'this' argument of 'void T::modify()' discards qualifiers
// const-object.modify() 不可以,reference-to-const.modify() 自然也不可以
rct2.modify(); // ERROR. passing 'const T' as 'this' argument of 'void T::modify()' discards qualifiers
// 虽然 object.modify() 可以,可 reference-to-const 不允许修改
}
const T
本身的值不能改- 即使你是把一个
T&
(t)赋给一个const T&
(rct2),你也不能通过这个const T&
去修改它的值,虽然你可以用T*
直接去修改(t.modify();)- 由此看来,
const T&
其实是一种契约精神!(说不能改就不能改) - 这一点和 C++: Const Pointer 是相同的
- 由此看来,
Comments