diff --git a/doc/00_optional.qbk b/doc/00_optional.qbk index e48c8e4..5ff5981 100644 --- a/doc/00_optional.qbk +++ b/doc/00_optional.qbk @@ -2,7 +2,7 @@ [quickbook 1.4] [authors [Cacciola Carballal, Fernando Luis]] [copyright 2003-2007 Fernando Luis Cacciola Carballal] - [copyright 2014-2021 Andrzej Krzemieński] + [copyright 2014-2022 Andrzej Krzemieński] [category miscellaneous] [id optional] [dirname optional] diff --git a/doc/23_ref_optional_io.qbk b/doc/23_ref_optional_io.qbk index a9425d4..2f9b5a2 100644 --- a/doc/23_ref_optional_io.qbk +++ b/doc/23_ref_optional_io.qbk @@ -26,11 +26,11 @@ template template std::basic_ostream& operator<<(std::basic_ostream& out, none_t const&); ``[link reference_operator_ostream_none __GO_TO__]`` - + template std::basic_istream& operator>>(std::basic_istream& in, optional& v); ``[link reference_operator_istream __GO_TO__]`` - + } // namespace boost ``` @@ -43,8 +43,8 @@ template `template ` [br] -\u00A0\u00A0\u00A0\u00A0`std::basic_ostream&` [br] -\u00A0\u00A0\u00A0\u00A0`operator<<(std::basic_ostream& out, optional const& v);` + std::basic_ostream&` [br] + operator<<(std::basic_ostream& out, optional const& v);` * [*Effect:] Outputs an implementation-defined string. The output contains the information about whether the optional object contains a value or not. If `v` contains a value, the output contains result of calling `out << *v`. * [*Returns:] `out`. @@ -53,8 +53,8 @@ __SPACE__ [#reference_operator_ostream_none] `template ` [br] -\u00A0\u00A0\u00A0\u00A0`std::basic_ostream&` [br] -\u00A0\u00A0\u00A0\u00A0`operator<<(std::basic_ostream& out, none_t);` + std::basic_ostream&` [br] + operator<<(std::basic_ostream& out, none_t);` * [*Effect:] Outputs an implementation-defined string. * [*Returns:] `out`. @@ -63,11 +63,11 @@ __SPACE__ [#reference_operator_istream] `template ` [br] -\u00A0\u00A0\u00A0\u00A0`std::basic_ostream&` [br] -\u00A0\u00A0\u00A0\u00A0`operator>>(std::basic_istream& in, optional& v);` + std::basic_ostream&` [br] + operator>>(std::basic_istream& in, optional& v);` * [*Requires:] `T` is __SGI_DEFAULT_CONSTRUCTIBLE__ and __MOVE_CONSTRUCTIBLE__. -* [*Effect:] Reads the value of optional object from `in`. If the string representation indicates that the optional object should contain a value, `v` contains a value and its contained value is obtained as if by default-constructing an object `o` of type `T` and then calling `in >> o`; otherwise `v` does not contain a value, and the previously contained value (if any) has been destroyed. +* [*Effect:] Reads the value of optional object from `in`. If the string representation indicates that the optional object should contain a value, `v` contains a value and its contained value is obtained as if by default-constructing an object `o` of type `T` and then calling `in >> o`; otherwise `v` does not contain a value, and the previously contained value (if any) has been destroyed. * [*Returns:] `out`. [endsect] diff --git a/doc/27_ref_optional_synopsis.qbk b/doc/27_ref_optional_synopsis.qbk index bcd18e8..9af4d76 100644 --- a/doc/27_ref_optional_synopsis.qbk +++ b/doc/27_ref_optional_synopsis.qbk @@ -73,6 +73,16 @@ } // namespace boost + namespace std { + + template + struct hash > ; ``[link reference_std_hash_spec __GO_TO__]`` + + template + struct hash > ; ``[link reference_std_hash_spec __GO_TO__]`` + + } // namespace std + [endsect] diff --git a/doc/28_ref_optional_semantics.qbk b/doc/28_ref_optional_semantics.qbk index 2fa7500..007d8f8 100644 --- a/doc/28_ref_optional_semantics.qbk +++ b/doc/28_ref_optional_semantics.qbk @@ -1408,3 +1408,37 @@ assert (addressof(*opt0) == addressof(y)); `` [endsect] + + +[section Detailed Semantics - std::hash Specializations] + +__SPACE__ +[#reference_std_hash_spec] + +`` +namespace std { + +template +struct hash > ; + +template +struct hash > ; + +} // namespace std +`` + +The specialization `hash>` is enabled if and only if +`hash>` is enabled. When enabled, for an object `o` +of type `optional`, if `o.has_­value() == true`, then `hash>()(o)` + evaluates to the same value as `hash>()(*o)`; otherwise it +evaluates to an unspecified value. +The member functions are not guaranteed to be `noexcept`. + +[caution +You may get compiler errors when your program provides specializations for +`std::hash>`. If this happens, define macro +`BOOST_OPTIONAL_CONFIG_DO_NOT_SPECIALIZE_STD_HASH` to suppress the specializations +of `std::hash` in this library. +] + +[endsect] diff --git a/doc/91_relnotes.qbk b/doc/91_relnotes.qbk index f1f4364..5c8fa3f 100644 --- a/doc/91_relnotes.qbk +++ b/doc/91_relnotes.qbk @@ -1,7 +1,7 @@ [/ Boost.Optional - Copyright (c) 2015 - 2021 Andrzej Krzemienski + Copyright (c) 2015 - 2022 Andrzej Krzemienski Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -11,6 +11,10 @@ [section:relnotes Release Notes] +[heading Boost Release 1.80] + +* [*Breaking change:] Added specializations for `std::hash>`. This fixes [@https://github.com/boostorg/optional/issues/55 issue #55]. You may get compiler errors when your program provides specializations for `std::hash>`. If this happens, define macro `BOOST_OPTIONAL_CONFIG_DO_NOT_SPECIALIZE_STD_HASH` to suppress the specializations of `std::hash` in this library. + [heading Boost Release 1.79] * Fixed [@https://github.com/boostorg/optional/issues/98 issue #98]. diff --git a/include/boost/optional/detail/optional_hash.hpp b/include/boost/optional/detail/optional_hash.hpp new file mode 100644 index 0000000..234ee7b --- /dev/null +++ b/include/boost/optional/detail/optional_hash.hpp @@ -0,0 +1,49 @@ +// Copyright (C) 2022 Andrzej Krzemienski. +// +// Use, modification, and distribution is subject to 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) +// +// See http://www.boost.org/libs/optional for documentation. +// +// You are welcome to contact the author at: +// akrzemi1@gmail.com + +#ifndef BOOST_OPTIONAL_DETAIL_OPTIONAL_HASH_AJK_20MAY2022_HPP +#define BOOST_OPTIONAL_DETAIL_OPTIONAL_HASH_AJK_20MAY2022_HPP + +#include +#include + +#if !defined(BOOST_OPTIONAL_CONFIG_DO_NOT_SPECIALIZE_STD_HASH) && !defined(BOOST_NO_CXX11_HDR_FUNCTIONAL) + +#include + +namespace std +{ + template + struct hash > + { + typedef std::size_t result_type; + typedef boost::optional argument_type; + + BOOST_CONSTEXPR result_type operator()(const argument_type& arg) const { + return arg ? std::hash()(*arg) : result_type(); + } + }; + + template + struct hash > + { + typedef std::size_t result_type; + typedef boost::optional argument_type; + + BOOST_CONSTEXPR result_type operator()(const argument_type& arg) const { + return arg ? std::hash()(*arg) : result_type(); + } + }; +} + +#endif // !defined(BOOST_OPTIONAL_CONFIG_DO_NOT_SPECIALIZE_STD_HASH) && !defined(BOOST_NO_CXX11_HDR_FUNCTIONAL) + +#endif // header guard diff --git a/include/boost/optional/optional.hpp b/include/boost/optional/optional.hpp index a04ac86..4134a7e 100644 --- a/include/boost/optional/optional.hpp +++ b/include/boost/optional/optional.hpp @@ -65,6 +65,7 @@ #include #include #include +#include namespace boost { namespace optional_detail { diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 477f4df..c965863 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -26,6 +26,7 @@ import testing ; [ run optional_test_empty_braces.cpp ] [ run optional_test_make_optional.cpp ] [ run optional_test_flat_map.cpp ] + [ run optional_test_hash.cpp ] [ run optional_test_map.cpp ] [ run optional_test_tie.cpp ] [ run optional_test_ref_assign_portable_minimum.cpp ] diff --git a/test/optional_test_hash.cpp b/test/optional_test_hash.cpp new file mode 100644 index 0000000..8a631a2 --- /dev/null +++ b/test/optional_test_hash.cpp @@ -0,0 +1,64 @@ +// Copyright (C) 2014 Andrzej Krzemienski. +// +// Use, modification, and distribution is subject to 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) +// +// See http://www.boost.org/lib/optional for documentation. +// +// You are welcome to contact the author at: +// akrzemi1@gmail.com + +#include "boost/optional/optional.hpp" +#include "boost/config.hpp" +#include "boost/core/lightweight_test.hpp" + +#if !defined(BOOST_NO_CXX11_HDR_UNORDERED_SET) && !defined(BOOST_OPTIONAL_CONFIG_DO_NOT_SPECIALIZE_STD_HASH) + +#include + +void test_unordered_map() +{ + std::unordered_set > set; + set.insert(boost::optional(1)); + set.insert(boost::optional(1)); + + + BOOST_TEST(set.size() == 1u); + BOOST_TEST(set.find(boost::optional(1)) != set.end()); +} + +#else + +void test_unordered_map() +{} + +#endif + + +#if !defined(BOOST_OPTIONAL_CONFIG_DO_NOT_SPECIALIZE_STD_HASH) && !defined(BOOST_NO_CXX11_HDR_FUNCTIONAL) + +void tets_hash() +{ + std::hash > hash_int; + boost::optional oN; + boost::optional o1(1); + + BOOST_TEST(hash_int(oN) == hash_int(oN)); + BOOST_TEST(hash_int(o1) == hash_int(o1)); +} + +#else + +void tets_hash() +{} + +#endif + + +int main() +{ + test_unordered_map(); + tets_hash(); + return boost::report_errors(); +}