
On Thu, Nov 13, 2008 at 7:43 PM, Eric Niebler <eric@boost-consulting.com> wrote:
I've noticed test failures when compiling some of boost in C++0x mode due to the change to boost::result_of to use decltype. The problem comes from function objects like this:
template<class T> struct identity { typedef T result_type;
T const & // <== problem here operator()(T const &t) const { return t; } };
In this (overly simplistic) example, we just want to return what is passed in. The computed type (result_type) is different than the return type of operator() ...
Not actually, no. For awhile now, the draft C++0x standard has specified the computed or deduced std::result_of<F()>::type to be the result type of a call to the function object F. A simple way to implement C++0x std::result_of is using decltype. This is a great improvement over the old intrusive protocol requiring function objects to advertise (or falsely advertise ;) their return types. On platforms that support decltype, boost::result_of behaves like the proposed draft standard std::result_of. Actually, it may be the only/first implementation...
and for a good (IMO) reason: to avoid an unnecessary copy in some cases. The idea is to accommodate usage like this:
result_of<identity(BigObj)>::type i = identity()(BigObj());
For completeness, the above line won't compile unless you include BigObj as a template parameter to identity. So the line should be result_of<identity<BigObj>(BigObj)>::type i = identity<BigObj>()(BigObj()); However, that's not actually what you want. You're not interested in the return type of identity, you're interested in the type of the template argument. With C++0x std::identity, for example, the template argument type is std::identity::type. So you could write: std::identity<BigObj>::type i = std::identity<BigObj>()(BigObj()); Or obviously, in this particular situation, you could just write: BigObj i = identity<BigObj>()(BigObj());
It is really very important that decltype(i) be BigObj, and not BogObj const &, which would cause a dangling reference. But with the change to use decltype in boost::result_of, the dangling reference is exactly what I would get.
Am I now forced to change identity::operator() return by value here?
No. In general, if you want to remove references from the result type of any arbitrary callable object F, you can write: remove_reference< result_of<F()>::type
::type i = F()();
This guarantees that i is not a dangling reference without requiring changes to F. Daniel Walker