
Bjorn Reese wrote
I think boost/serialization/list.hpp checks for the default constructor in the wrong way. If you use std::vector instead of std::list, then it compiles.
Unfortunately this isn't exactly true. I had it using an std::vector before using std::list, which I ultimately sent and it fails as well. I have checked std::list, std::vector, std::forward_list, std::unordered_map, std::map, and std::unordered_set. Robert Ramey wrote
I've recently spent some time on this and I believe that I've now fixed it. I had some difficulty with "is_default_constructible".
Unfortunately this isn't where the problem is coming from, at least not on MSVC++2013. The problem with std::vector and std::list (and the other sequential, non-associative containers) is that regardless of is_default_contructible<T> with T being non-default constructible, the compiler will generate code that calls T::T() and will have a compiler error because of it. Specifically, you call std::vector::resize, std::list::resize and std::forward_list::resize with the second argument being default which results in the generation of code calling T::T(). Inside boost/serialization/list.hpp and the load function: if(detail::is_default_constructible<U>()){ t.resize(count); /////////////////////////////////////////////////////// <- here typename std::list<U, Allocator>::iterator hint; hint = t.begin(); while(count-- > 0){ ar >> boost::serialization::make_nvp("item", *hint++); } } The problem is when U is non-default constructible. That if will never stop the code generation under it even if is_default_constructible<U> will always be false. std::list::resize has the signature "void resize (size_type n, value_type val = value_type())"; this is where the compiler error originates from, at least for std::list, std::vector and std::forward_list. The best solution I thought of would be to change the basic if to a tag dispatch method, or to utilize SFINAE, to preclude the compiler from generating code calling U::U(). std::vector also has an additional layer above it using tag dispatch to select between unoptimized and optimized algorithms. The above will work for both of those algorithms (the problem is the same, IE they're both calling resize). The problem for the associative containers is a bit different, but (potentially?) easier to fix. Inside of boost\serialization\unordered_map.hpp: struct archive_input_unordered_map { inline void operator()( Archive &ar, Container &s, const unsigned int v ){ typedef typename Container::value_type type; detail::stack_construct<Archive, type> t(ar, v); //////////////////////////////////// <- here // borland fails silently w/o full namespace ar >> boost::serialization::make_nvp("item", t.reference()); std::pair<typename Container::const_iterator, bool> result = s.insert(t.reference()); // note: the following presumes that the map::value_type was NOT tracked // in the archive. This is the usual case, but here there is no way // to determine that. if(result.second){ ar.reset_object_address( & (result.first->second), & t.reference().second ); } } }; Type = std::unordered_map::value_type, which is an std::pair<Key, Value>. detail::stack_construct calls boost::serialization::load_construct_data_adl and because you're giving it an std::pair type, it'll call std::pair's load_contruct_data; std::pair does not have a load_construct_data, so it calls the default constructor - that default constructor in turn calls the default constructors for both elements it watches over, and in this case one of those types is non-default constructible. The solution I came up with to fix this was to not serialize the std::pair's and instead just serialize the two elements directly. This has fixed this problem and I am now able to serialize non-default constructible types in an std::unordered_map. The code below is the new loader: template<class Archive, class Container> struct archive_input_unordered_map { inline void operator()(Archive &ar, Container &s, const unsigned int v) { typedef typename Container::key_type First; typedef typename Container::mapped_type Second; // borland fails silently w/o full namespace detail::stack_construct<Archive, First> first(ar, v); ar >> boost::serialization::make_nvp("first", first.reference()); detail::stack_construct<Archive, Second> second(ar, v); ar >> boost::serialization::make_nvp("second", second.reference()); std::pair<typename Container::const_iterator, bool> result = s.emplace(std::move(first.reference()), std::move(second.reference())); // note: the following presumes that the map::value_type was NOT tracked // in the archive. This is the usual case, but here there is no way // to determine that. if(result.second) ar.reset_object_address(&(result.first->second), &second.reference()); } }; One would also have to change the saving code to reflect the new loading above. I'm not sure if that works for all cases. It might not be savory because of the elimination of the item nvp for XML. I also had to add in the std::move's to work with std::unique_ptr's, not sure if your usage of insert would differ from my usage of emplace to impact that in any way, though; obviously these would need more testing. -- View this message in context: http://boost.2283326.n4.nabble.com/Serialization-STL-Containers-and-Non-Defa... Sent from the Boost - Users mailing list archive at Nabble.com.