编译器的差异:临时对象做实参的可能编译不过?
下面的代码在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++中不存在无效引用。出了作用域,临时对象与引用一同消失。
这里第二句是关键:向一变量赋值可能导致赋值到一个马上就要消失的临时变量上去,而对于常量引用不存在这个问题。
很明显,问题就在于,向一个马上就要消失的变量赋值没有起到期望的作用,常量引用没问题是因为常量引用不允许赋值。