diff --git a/include/boost/serialization/optional.hpp b/include/boost/serialization/optional.hpp index bc6d82a1e..ce1d364de 100644 --- a/include/boost/serialization/optional.hpp +++ b/include/boost/serialization/optional.hpp @@ -20,6 +20,7 @@ #include #endif +#include #include #include #include @@ -83,7 +84,7 @@ void load_impl( } typename OT::value_type t; ar >> boost::serialization::make_nvp("value",t); - ot = t; + ot = boost::move(t); } } // detail diff --git a/test/test_optional.cpp b/test/test_optional.cpp index e12605433..42b6cc3b3 100644 --- a/test/test_optional.cpp +++ b/test/test_optional.cpp @@ -2,6 +2,7 @@ // test_optional.cpp // (C) Copyright 2004 Pavel Vozenilek +// Copyright 2026 Gennaro Prota // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) @@ -92,6 +93,59 @@ int test(){ return EXIT_SUCCESS; } +// A move-only value type: deleted copy, defaulted move. Loading an +// optional must move the deserialized value into place rather than copy +// it. +#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) \ + && !defined(BOOST_NO_CXX11_DELETED_FUNCTIONS) \ + && !defined(BOOST_NO_CXX11_DEFAULTED_FUNCTIONS) +#define BOOST_SERIALIZATION_TEST_OPTIONAL_MOVE_ONLY + +struct M { + int m_x; + M() : m_x(0) {} + explicit M(int x) : m_x(x) {} + M(const M &) = delete; + M & operator=(const M &) = delete; + M(M &&) = default; + M & operator=(M &&) = default; + template + void serialize(Archive & ar, const unsigned int /* version */){ + ar & boost::serialization::make_nvp("x", m_x); + } +}; + +template class Optional> +int test_move_only(){ + const char * testfile = boost::archive::tmpnam(NULL); + BOOST_REQUIRE(NULL != testfile); + + Optional o_empty; + Optional o_value(M(42)); + { + test_ostream os(testfile, TEST_STREAM_FLAGS); + test_oarchive oa(os, TEST_ARCHIVE_FLAGS); + oa << boost::serialization::make_nvp("o_empty", o_empty); + oa << boost::serialization::make_nvp("o_value", o_value); + } + // start each target in the opposite state, so load must both reset and + // assign + Optional o_empty_a(M(7)); + Optional o_value_a; + { + test_istream is(testfile, TEST_STREAM_FLAGS); + test_iarchive ia(is, TEST_ARCHIVE_FLAGS); + ia >> boost::serialization::make_nvp("o_empty", o_empty_a); + ia >> boost::serialization::make_nvp("o_value", o_value_a); + } + BOOST_CHECK(! o_empty_a); + BOOST_CHECK(static_cast(o_value_a) && 42 == o_value_a->m_x); + + std::remove(testfile); + return EXIT_SUCCESS; +} +#endif // move-only support available + #include #ifndef BOOST_NO_CXX17_HDR_OPTIONAL #include @@ -102,5 +156,11 @@ int test_main( int /* argc */, char* /* argv */[] ){ #ifndef BOOST_NO_CXX17_HDR_OPTIONAL test(); #endif + #ifdef BOOST_SERIALIZATION_TEST_OPTIONAL_MOVE_ONLY + test_move_only(); + #ifndef BOOST_NO_CXX17_HDR_OPTIONAL + test_move_only(); + #endif + #endif return EXIT_SUCCESS; }