[Iterators] Change in transform_iterator from 1.31 to 1.32?

We have a class that contains a std::map. This class wants to provide its clients with iterators over just the values in the map, hiding the fact that there's a map under the covers at all. transform_iterator seems like just the ticket, and with Boost 1.31 we used these methods: typedef std::map<KEYTYPE, VALUETYPE> MapType; typedef boost::transform_iterator< boost::function<const MapType::mapped_type&(const MapType::value_type&)>, MapType::const_iterator > const_iterator; const_iterator begin() const { return boost::make_transform_iterator(mymap.begin(), boost::bind(&MapType::value_type::second, _1)); } const_iterator end() const { return boost::make_transform_iterator(mymap.end(), boost::bind(&MapType::value_type::second, _1)); } The above compiled cleanly, except when it caused a compiler ICE, and the instances that compile seem to run okay. Now we're trying to upgrade to Boost 1.32 -- I know, a little late in the 1.32 lifespan. The same code now produces an ominous warning (full text at end of message): c:/videobranch\boost\boost\function\function_template.hpp(111) : warning C4172: returning address of local variable or temporary When it finally gets around to mentioning a line number in our own source code, the indicated line is the return statement in the above begin() method. The reason I'm concerned is that when compiled with Boost 1.32, our program suffers mysterious crashes that it did not when compiled with Boost 1.31. :-( I don't *know* that this warning is related, but I thought it prudent to try to eliminate such warnings to see if that might help. I can't figure out what temporary is driving the warning, though. I tried factoring out our own code a couple of different ways without making the warning go away. Can you suggest anything? Platform: Microsoft Windows XP Pro Compiler: Microsoft Visual C++ .NET, aka 7.1 *** Message ends; what follows is Microsoft warning output *** cl /EHsc /I c:/videobranch/boost $FF Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 13.10.3077 for 80x86 Copyright (C) Microsoft Corporation 1984-2002. All rights reserved. WrapMap.cpp c:/videobranch\boost\boost\function\function_template.hpp(111) : warning C4172: returning address of local variable or temporary c:/videobranch\boost\boost\function\function_template.hpp(109) : while compiling class-template member function 'const int boost::detail::function::function_obj_invoker1<FunctionObj,R,T0>::invoke (boost::detail::function::any_pointer,T0)' with [ FunctionObj=boost::_bi::bind_t<int,boost::_mfi::dm<int,std::pair<const std::string,int>>,boost::_bi::list1<boost::_bi::list_av_1<boost::arg<1>> ::B1>>, R=const int &, T0=const std::pair<const std::string,int> & ] c:/videobranch\boost\boost\function\function_template.hpp(482) : see reference to class template instantiation 'boost::detail::function::function_obj_invoker1<FunctionObj,R,T0>' being compiled with [ FunctionObj=boost::_bi::bind_t<int,boost::_mfi::dm<int,std::pair<const std::string,int>>,boost::_bi::list1<boost::_bi::list_av_1<boost::arg<1>> ::B1>>, R=const int &, T0=const std::pair<const std::string,int> & ] c:/videobranch\boost\boost\function\function_template.hpp(433) : see reference to function template instantiation 'void boost::function1<R,T0,Allocator>::assign_to<Functor>(FunctionObj,boost:: detail::function::function_obj_tag)' being compiled with [ R=const int &, T0=const std::pair<const std::string,int> &, Allocator=std::allocator<void>, Functor=boost::_bi::bind_t<int,boost::_mfi::dm<int,std::pair<const std::string,int>>,boost::_bi::list1<boost::_bi::list_av_1<boost::arg<1>> ::B1>>, FunctionObj=boost::_bi::bind_t<int,boost::_mfi::dm<int,std::pair<const std::string,int>>,boost::_bi::list1<boost::_bi::list_av_1<boost::arg<1>> ::B1>> ] c:/videobranch\boost\boost\function\function_template.hpp(289) : see reference to function template instantiation 'void boost::function1<R,T0,Allocator>::assign_to<Functor>(Functor)' being compiled with [ R=const int &, T0=const std::pair<const std::string,int> &, Allocator=std::allocator<void>, Functor=boost::_bi::bind_t<int,boost::_mfi::dm<int,std::pair<const std::string,int>>,boost::_bi::list1<boost::_bi::list_av_1<boost::arg<1>> ::B1>> ] c:/videobranch\boost\boost\function\function_template.hpp(649) : see reference to function template instantiation 'boost::function1<R,T0,Allocator>::function1<UnaryFunction>(Functor,boos t::enable_if_c<B,T>::type)' being compiled with [ R=const int &, T0=const std::pair<const std::string,int> &, Allocator=std::allocator<void>, UnaryFunction=boost::_bi::bind_t<int,boost::_mfi::dm<int,std::pair<const std::string,int>>,boost::_bi::list1<boost::_bi::list_av_1<boost::arg<1>> ::B1>>, Functor=boost::_bi::bind_t<int,boost::_mfi::dm<int,std::pair<const std::string,int>>,boost::_bi::list1<boost::_bi::list_av_1<boost::arg<1>> ::B1>>, B=true, T=int ] c:/videobranch\boost\boost\iterator\transform_iterator.hpp(130) : see reference to function template instantiation 'boost::function<Signature>::function<UnaryFunction>(Functor,boost::enab le_if_c<B,T>::type)' being compiled with [ Signature=const std::map<std::string,int>::mapped_type &(const std::map<std::string,int>::value_type &), UnaryFunction=boost::_bi::bind_t<int,boost::_mfi::dm<int,std::pair<const std::string,int>>,boost::_bi::list1<boost::_bi::list_av_1<boost::arg<1>> ::B1>>, Functor=boost::_bi::bind_t<int,boost::_mfi::dm<int,std::pair<const std::string,int>>,boost::_bi::list1<boost::_bi::list_av_1<boost::arg<1>> ::B1>>, B=true, T=int ] WrapMap.cpp(20) : see reference to function template instantiation 'boost::transform_iterator<UnaryFunction,Iterator>::transform_iterator<b oost::_bi::bind_t<R,F,L>,std::_Tree<_Traits>::const_iterator,boost::use_ default,boost::use_default>(const boost::transform_iterator<boost::_bi::bind_t<R,F,L>,Iterator,Reference,V alue> &,boost::iterators::enabled<__formal>::base<T>::type *)' being compiled with [ UnaryFunction=boost::function<const std::map<std::string,int>::mapped_type &(const std::map<std::string,int>::value_type &)>, Iterator=std::map<std::string,int>::const_iterator, R=int, F=boost::_mfi::dm<int,std::pair<const std::string,int>>, L=boost::_bi::list1<boost::_bi::list_av_1<boost::arg<1>>::B1>, _Traits=std::_Tmap_traits<std::string,int,std::less<std::string>,std::al locator<std::pair<const std::string,int>>,false>, Reference=boost::use_default, Value=boost::use_default, __formal=true, T=boost::detail::enable_type ]

Nat Goodspeed wrote:
We have a class that contains a std::map. This class wants to provide its clients with iterators over just the values in the map, hiding the fact that there's a map under the covers at all. transform_iterator seems like just the ticket, and with Boost 1.31 we used these methods:
typedef std::map<KEYTYPE, VALUETYPE> MapType;
typedef boost::transform_iterator< boost::function<const MapType::mapped_type&(const MapType::value_type&)>, MapType::const_iterator
const_iterator;
const_iterator begin() const { return boost::make_transform_iterator(mymap.begin(), boost::bind(&MapType::value_type::second, _1)); }
const_iterator end() const { return boost::make_transform_iterator(mymap.end(), boost::bind(&MapType::value_type::second, _1)); }
The above compiled cleanly, except when it caused a compiler ICE, and the instances that compile seem to run okay.
Now we're trying to upgrade to Boost 1.32 -- I know, a little late in the 1.32 lifespan. The same code now produces an ominous warning (full text at end of message):
c:/videobranch\boost\boost\function\function_template.hpp(111) : warning C4172: returning address of local variable or temporary
When it finally gets around to mentioning a line number in our own source code, the indicated line is the return statement in the above begin() method.
The change that's breaking your code is (unfortunately) in boost::bind, not in transform_iterator. In 1.31, boost::bind(&MapType::value_type::second, _1) returns a reference. In 1.32, the same construct returns by value. You can change boost::function<const MapType::mapped_type&(const MapType::value_type&)> to boost::function<MapType::mapped_type(const MapType::value_type&)> to return by value, or change boost::bind(&MapType::value_type::second, _1) to boost::bind<const MapType::mapped_type&>(&MapType::value_type::second, _1) to return by reference. boost::bind was changed to return by value because the old behavior suffered from a similar reference-to-temporary problem with nested binds (that didn't even produce a warning.)

"Peter Dimov" <pdimov@mmltd.net> writes:
c:/videobranch\boost\boost\function\function_template.hpp(111) : warning C4172: returning address of local variable or temporary
The first thing for you to know is that vc7.1 gave that warning erroneously in many cases. In this case, however, it seems correct.
When it finally gets around to mentioning a line number in our own source code, the indicated line is the return statement in the above begin() method.
The change that's breaking your code is (unfortunately) in boost::bind, not in transform_iterator. In 1.31, boost::bind(&MapType::value_type::second, _1) returns a reference. In 1.32, the same construct returns by value.
If Nat were using the actual type of the bind expression as the first argument to transform_iterator, this wouldn't be a problem (except -- not sure if you care -- that it would be returned by value from the iterator). Presumably, the bind object has an appropriate non-reference nested result_type. Nat, what you are doing looks _really_ inefficient. The result of make_transform_iterator is some iterator type that's very different from, but convertible to, your const_iterator type. Going through that extra layer using boost::function means you are doing a lot of extra copying -- and potentially, dynamic memory allocation -- whenever you ask for an iterator. HTH, -- Dave Abrahams Boost Consulting www.boost-consulting.com

Nat Goodspeed writes:
We have a class that contains a std::map. This class wants to provide its clients with iterators over just the values in the map, hiding the fact that there's a map under the covers at all. transform_iterator seems like just the ticket, and with Boost 1.31 we used these methods:
[...]
The reason I'm concerned is that when compiled with Boost 1.32, our program suffers mysterious crashes that it did not when compiled with Boost 1.31. :-(
See http://article.gmane.org/gmane.comp.lib.boost.devel/124603. -- Aleksey Gurtovoy MetaCommunications Engineering
participants (4)
-
Aleksey Gurtovoy
-
David Abrahams
-
Nat Goodspeed
-
Peter Dimov