编译器的差异:临时对象做实参的可能编译不过?
下面的代码在vs2005可正常编译运行,但gcc似乎不行(偶用的cygwin),虽然vs2005对标准C++的支持已经有很大进步了。。
gcc版本如下:
D:\cygwin\bin> gcc -v
Reading specs from /usr/lib/gcc/i686-pc-cygwin/3.4.4/specs
Configured with: /gcc/gcc-3.4.4/gcc-3.4.4-1/configure --verbose --prefix=/usr --exec-prefix=/usr --sysconfdir=/etc --lib
dir=/usr/lib --libexecdir=/usr/lib --mandir=/usr/share/man --infodir=/usr/share/info --enable-languages=c,ada,c++,d,f77,
java,objc --enable-nls --without-included-gettext --enable-version-specific-runtime-libs --without-x --enable-libgcj --d
isable-java-awt --with-system-zlib --enable-interpreter --disable-libgcj-debug --enable-threads=posix --enable-java-gc=b
oehm --disable-win32-registry --enable-sjlj-exceptions --enable-hash-synchronization --enable-libstdcxx-debug : (reconfi
gured)
Thread model: posix
gcc version 3.4.4 (cygming special) (gdc 0.12, using dmd 0.125)
言归正传,
问题一:
void fun2(string &str)
{
cout < < str < < endl;
}
int main()
{
fun2(string( "hello, "));
}
gcc编译错误提示如下:
D:\cygwin\home\ADMINI~1\project\aboutCPP.cpp: In function `int main() ':
D:\cygwin\home\ADMINI~1\project\aboutCPP.cpp:35: error: invalid initialization of non-const reference of type 'std::string& ' from a temporary of type 'std::string '
D:\cygwin\home\ADMINI~1\project\aboutCPP.cpp:28: error: in passing argument 1 of `void fun2(std::string&) '
为什么非法?看起来似乎是由于临时对象是一个const对象?真的吗?
答案是否定的,下面的代码可以说明之:
class MyString
{
public:
int Data() { return m_data; }
MyString &Data(int val) { m_data = val; }
private:
int m_data;
};
int main()
{
cout < < MyString().Data(2).Data() < < endl;
}
说明临时对象是non-const的!
另外,无意中又发现一个问题。
问题二:
class MyString
{
public:
int Data() { return m_data; }
MyString &Data(int val) { m_data = val; }
private:
int m_data;
};
int main()
{
MyString conMyStr;
const MyString conMyStr2;
}
gcc编译报错如下:
D:\cygwin\home\ADMINI~1\project\aboutCPP.cpp: In function `int main() ':
D:\cygwin\home\ADMINI~1\project\aboutCPP.cpp:51: error: uninitialized const `conMyStr2 '
gcc对const的支持有问题吗(瞎猜的,怎可能)?这个错又怎么解释?
小弟比较穷,分不多,但暂时也找不到好的解释办法。
哪位大侠可指点一下,谢谢~!
[解决办法]
关于标准的解释
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).
[解决办法]
to Jofee(阿飞) ,请注意这一段的理解:
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
第一句说的易出错指的是逻辑错误,而不是“临时对象销毁之后引用无效”,如果我没记错的话,C++中不存在无效引用。出了作用域,临时对象与引用一同消失。
这里第二句是关键:向一变量赋值可能导致赋值到一个马上就要消失的临时变量上去,而对于常量引用不存在这个问题。
很明显,问题就在于,向一个马上就要消失的变量赋值没有起到期望的作用,常量引用没问题是因为常量引用不允许赋值。