سؤال

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.

نصائح أخرى

In #2, a temporal tuple<double const&> is constructed from tuple<int> as the argument n for foo. Before tuple<double const&>'s construction, a temporal double(D) is constructed from an int member of tuple<int>, and the double const& member is initialized as D. Temporal objects constructed to prepare function arguments are destructed when the function call is completed. So, D is destructed when tuple<double const&>'s constructor finishes.

Hope this helps

I believe line #1 is OK because const references are allowed to refer to temporaries. Somebody can probably quote the standard on that.

Preface: I am not a Boost or Tuple expert. I've never used either. I will attempt to be helpful anyway.

It appears to me that boost::make_tuple(2) is returning a tuple<int>. However, there appears to be an implicit cast done to match your foo implementation, during which time it may be attempting to take the address of the temporary (converting int to const double&) inappropriately.

Have you tried explicitly casting your make_tuple?

Again, I'm not a boost or tuple expert, but I would try:

 foo(boost::make_tuple(boost::cref((double)2)));//#2

or

 foo(boost::make_tuple((const double&)2);//#2

I admit I'm taking an educated guess here in an attempt to help, so I don't promise this is the right tree to bark up.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top