大卫的思维空间

自由的国度,思维的空间......
随笔 - 67, 文章 - 3, 评论 - 117, 引用 - 11

导航

<2004年11月>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

公告

大卫语:
我不是天生的王者,除了奋斗,我别无选择!
David's Motto:
Since I am not an inborn victor, I have no choice but strive!

留言簿(6)

随笔分类

随笔档案

文章分类

文章档案

相册

相关链接

搜索

最新评论

阅读排行榜

评论排行榜

关于const reference的几点说明

几天前,在程序中遇到一个问题,当我检查程序错误时,在STL实现中发现了类似下面的代码:
#include <iostream>
using namespace std;

class
 A
{

public
:
    A(const string& s)
    {

        cout << s.c_str() << endl;
    }
};


int
 main()
{

    const
 char* psz = "abc";
    A a(psz);

    return
 0;
}

对此,我感到很奇怪,因为我从来没有这样做过,怎么能把一个引用指向一个与引用本身类型不同的类型呢?于是,我在网上发了一个帖子,很快,有人从CPL 3rd中摘录了以下内容作为回复:

// Begin
Initialization of a reference is trivial when the initializer is an 
lvalue (an object whose address you can take; see §4.9.6). The 
initializer for a 'plain' T& must be an lvalue of type T. The 
initializer for a const T& need not be an lvalue or even of type T. 
In such cases (David''s notes: and only in such cases),
[
1] first, implicit type conversion to T is applied if necessary (see §C.6);
[
2] then, the resulting value is placed in a temporary variable of type T; and
[
3] finally, this temporary variable is used as the value of the initializer.
Consider:
double
& dr = 1; // error: lvalue needed
const double& cdr = 1; // ok
The interpretation of this last initialization might be:
double
 temp = double(1) ; // first create a temporary with the right value
const double& cdr = temp; // then use the temporary as the initializer for cdr
A temporary created to hold a reference initializer persists until 
the end of its reference’s scope.
References to variables and references to constants are distinguished 
because the introduction of a temporary in the case of the variable 
is highly errorprone;
an assignment to the variable would become an assignment to the – 
soon to disappear – temporary. No such problem exists for references 
to constants, and references to constants are often important as 
function arguments (§11.6).
// End

也就是说,只要存在可行的转换路径,编译器在构造引用的时候会创建一个与引用类型类型信息相同的临时变量,然后,用引用指向该临时变量.同时,上面这段文字还给了我们如下信息:
1.
const reference不会享受这样的特殊待遇.如下面的代码是错误的:
float
 f = 0;
double
& d = f;

char
* psz = "abc";
string& rstr = psz;
(
: VC6在编译以上各句时报告了一个与实际不符的错误提示:A reference that is not to 'const' cannot be bound to a non-lvalue,这里的f和psz明明是左值)
2.
const reference会使函数的适用性更强,但代价是在进行类型转换时可能引入较大的开销,失去引用传递的高效性.

当然,除此之外,const只是一个编译期的概念,public/protected/private等一样,只是告诉编译器,我们需要针对这些标示之后的东西添加一些附加的描述,请区别对待.这些描述对于private而言,是说:进行语法检查时如果发现非本类或friend访问了这些成员(方法),则报告错误;对于const而言,则是说:如果语法检查时发现有任何操作对const所修饰的对象进行了修改,则报告错误.
在语法检查之后,这些修饰符就该悄然引退了.

posted on 2004-11-11 09:54 大卫的思维空间 阅读(4525) 评论(6)  编辑 收藏

评论

# 这个属于隐士转换

看看more effective c++ 。上面有介绍(或看explicit关键词的介绍,可能也有说明)。
2005-04-21 15:35 | 清风雨

# 这个转换和引用没有 必然关系。其它比如赋值、copy构造 操作等也会的。

内容不能为空。
2005-04-21 15:38 | 清风雨

# to 楼上

你的回复让我觉得自己白说了,这个确实跟隐式转换有关系:
float f = 0;
double d = f;
这叫隐式转换,但加上个&,就不能隐式转换了,编译器会complain的,必须是const reference才OK:
float f = 0;
const double& d = f;
我要说的是这个,必须跟reference搅和在一起才有我讨论的问题,如果你非说跟引用没关系,那就不用讨论了,^_^。

赋值确实可执行隐式转换,但跟上面的问题完全是两回事(如果涉及引用就正好是一回事了)。但对于拷贝构造函数需要注意这个问题,如:
class A
{
public:
A(const double&)
{
}
};

int main()
{
float f = 0;
A a(f);
A b = f;
}
如果去掉A(const double&)中的const,会编译失败。因为存在文章中讨论的问题,这从一定程度上提示我们,对于拷贝构造函数:
1、由于拷贝构造函数一般不应该修改参数,所以,一般应该在参数前面加上const;
2、如果加上const,可支持隐式转换,反之,则没有;
3、如果你不希望支持隐式转换,去掉const,但1就无法满足了。
2005-05-20 10:50 | Bill David

# re: 关于const reference的几点说明

希望兄弟冷帮忙解决一个关于用指针强制修改const 常量的麻烦问题
源程序如下:



#include<iostream.h>


int main()
{
const int i=10;
const int *p=&i;
int*pr1=(int*)&i;


cout<<"begin const int i="<<i<<'\n'<<"lable of i is\t"<<&i<<endl
    <<"let const int *p=&i"<<'\n'<<"value of p is\t"<<p<<endl
    <<"*p="<<*p<<endl
<<"pr1=(int*)&i"<<endl
<<"value of pr1 is\t"<<pr1<<endl
<<"*pr1="<<*pr1<<endl<<endl;


*pr1=20;


cout<<"\nafter do \"*pr1=20\" then:\n"
    <<"i="<<i<<endl
    <<"*p="<<*p<<endl
<<"*pr1="<<*pr1<<endl;


p=&i;
pr1=(int*)&i;


cout<<"\n\ndo \"p=&i\" again\n"
    <<"do \"pr1=(int*)&i\" again"<<endl
    <<"lable of i is\t"<<&i<<endl
<<"value of p is\t"<<p<<endl
<<"value of pr1 is\t"<<pr1<<endl
    <<"i="<<i<<endl
    <<"*p="<<*p<<'\n'
<<"*pr1="<<*pr1<<endl<<endl
<<"更奇怪的是:"<<endl
    <<"*(&i)="<<*(&i)<<endl
<<"*(p=&i)="<<*(p=&i)<<endl
<<"*p="<<*p<<endl<<endl;


cout<<"why is this???"<<endl;
cin.get();
return 0;
}





输出结果如下:


begin const int i=10
lable of i is   0x0012FF7C
let const int *p=&i
value of p is   0x0012FF7C
*p=10
pr1=(int*)&i
value of pr1 is 0x0012FF7C
*pr1=10


after do "*pr1=20" then:
i=10
*p=20
*pr1=20


do "p=&i" again
do "pr1=(int*)&i" again
lable of i is   0x0012FF7C
value of p is   0x0012FF7C
value of pr1 is 0x0012FF7C
i=10
*p=20
*pr1=20

更奇怪的是:
*(&i)=10
*(p=&i)=20
*p=20

why is this???


希望兄弟能帮忙解决,多谢
2006-05-31 20:43 | a___9

# to a___9

to 楼上,一句话:由于i是const,所有直接输出i的地方在编译时已经被替换成10写进目标代码里了。

所以,不管你怎么改&i的内容,看到的都是10。
至于*(&i),也输出10,则是被编译器优化还原成i的结果。
如果你懂汇编(其实,无需全懂,^_^),把各输出语句拆开,看一下汇编码就清楚了。
2006-06-08 11:30 | Bill David

# re: 关于const reference的几点说明

多谢指教!
2006-06-16 17:27 | a___9
标题  
姓名  
主页
验证码 *
内容   
  登录  使用高级评论  Top
[使用Ctrl+Enter键可以直接提交]