I have seen this question. It seems that regardless of the cast, the temporary object(s) will "survive" until the fullexpression evaluated. But in the following scenario:
template<class T>
struct bar {
T t;
bar(T t) : t(t) {}
template<class U>
bar(bar<U> other) : t(other.t) {}
};
void foo(bar<const double&> b) {
printf("%lf\n", b.t);
}
int main() {
foo(bar<const double&>(2));//#1
foo(bar<int>(2)); //#2
return 0;
}
1 run well, but 2 do not. And MSVC gave me a warning about 2: "reference member is initialized to a temporary that doesn't persist after the constructor exits"
Now I am wondering why they both make a temporary double
object and pass it to bar<const double&>
and only 2 failed.
@update
I use struct bar instead of boost::tuple
in the original post, hope it will be more familiar to others.
Let me make my question more clear. In #1, a temporal double
is created from int
(2) and then a bar<const double &>
is created from it and copied into foo
, while in #2, a temporal bar<int>
is created and a temporal double
is created from the member of bar<int>
in the ctor of bar<const double&>
. It seems that the temporal double
is destructed in foo
in #2 while do not in #1. Why? I think they are all part of the fullexpression and shall be exist until bar
return.
Tim says "The compiler is smart enough to treat this 2 as a double instead of an int.". so I wrote int i = 2;
and passed i
to both of the two calls, but things go on like before. I made it in VS2008 with debug mode.
.#1 calls boost::tuple<const double&>::tuple(const double&)
. In order to do this, a temporary double
is created by the full-expression foo(boost::tuple<const double&>(2))
. Then a temporary boost::tuple<const double&>
is created. It has a reference member which is bound to the temporary double
. Both temporaries exist until full-expression #1 is done, and is still valid when foo
is called.
.#2 calls boost::tuple<const double&>::tuple(const boost::tuple<int>&)
. This expression creates a temporary boost::tuple<int>
. The lifetime of that temporary is similarly not a problem. But consider what happens when that tuple
constructor is called. Simplified / pseudocode classes:
template<> class tuple<int> {
private:
int member1_;
//...
};
template<> class tuple<const double&> {
private:
const double& member1_;
public:
tuple(const tuple<int>& int_tup) : member1_(int_tup.member1_) {}
// ...
};
The mem-initializer member1(int_tup.member1_)
converts the int
value to a temporary double
and binds that double
to the class reference member. This temporary double
is created by the full-expression member1_(int_tup.member1_)
, not by the full-expression foo(boost::make_tuple(2))
. A special exception for mem-initializers guarantees that the temporary double
is okay until the end of the constructor in which it was created, but then there's no guarantee it's still valid when foo
is called.
So the important difference is that statement #1 creates the temporary double
itself, but statement #2 indirectly causes a temporary double
to be created within another function. Exactly which full-expression creates a temporary has an impact on how long that temporary will live.