From cf18d2bbac0b681fe3b6ce4951d67c4dbe7a935e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ion=20Gazta=C3=B1aga?= Date: Tue, 26 Aug 2014 23:23:12 +0200 Subject: [PATCH] is_copy_assignable implementation added. It needs C++11 support to work reliable. --- doc/is_copy_assignable.qbk | 51 ++++ doc/type_traits.qbk | 1 + include/boost/type_traits.hpp | 1 + .../boost/type_traits/is_copy_assignable.hpp | 147 ++++++++++++ test/is_copy_assignable.cpp | 223 ++++++++++++++++++ 5 files changed, 423 insertions(+) create mode 100644 doc/is_copy_assignable.qbk create mode 100644 include/boost/type_traits/is_copy_assignable.hpp create mode 100644 test/is_copy_assignable.cpp diff --git a/doc/is_copy_assignable.qbk b/doc/is_copy_assignable.qbk new file mode 100644 index 0000000..e1de31a --- /dev/null +++ b/doc/is_copy_assignable.qbk @@ -0,0 +1,51 @@ +[/ + Copyright 2007 John Maddock. + Copyright 2014 Ion Gaztanaga. + 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). +] + +[section:is_copy_assignable is_copy_assignable] + + template + struct is_copy_assignable : public __tof {}; + +__inherit If `T` is `CopyAssignable` (i.e. has an accessible explicit or implicit copy assignment operator), +then inherits from __true_type, otherwise inherits from __false_type. Type `T` +must be a complete type. + +In other words, inherits from __true_type only if copy assignment of `T` from `const T &` is not +marked with `= delete`, `T` does not derives from `boost::noncopyable` and +is not marked with `BOOST_MOVABLE_BUT_NOT_COPYABLE(T)`. + +__compat If the compiler does not support partial-specialization of class +templates, then this template can not be used. + +If your compiler does not support C++11 deleted functions (`= delete`) or does not support +SFINAE for the deleted assignments, then derive your classes from `boost::noncopyable` or +mark them with `BOOST_MOVABLE_BUT_NOT_COPYABLE(T)` to show that class is non-assignable. + +Trait does not care about access modifiers, so if you see errors like this: + + 'T::operator=(const T&)' is private + boost/type_traits/is_copy_assignable.hpp:68:5: error: within this context + +then you are trying to call that macro for a structure with private assignment: + + struct T { + // ... + private: + T &operator=(const T &); + // ... + }; + +To fix that you must modify your structure, explicitly marking it as noncopyable (`= delete`, +`boost::noncopyable` or `BOOST_MOVABLE_BUT_NOT_COPYABLE(T)`) or explicitly +[link boost_typetraits.user_defined specializing the trait]. + + +__header ` #include ` or ` #include ` + +[endsect] + diff --git a/doc/type_traits.qbk b/doc/type_traits.qbk index cbb4fd6..816f7af 100644 --- a/doc/type_traits.qbk +++ b/doc/type_traits.qbk @@ -69,6 +69,7 @@ [def __is_empty [link boost_typetraits.reference.is_empty is_empty]] [def __is_const [link boost_typetraits.reference.is_const is_const]] [def __is_copy_constructible [link boost_typetraits.reference.is_copy_constructible is_copy_constructible]] +[def __is_copy_assignable [link boost_typetraits.reference.is_copy_assignable is_copy_assignable]] [def __is_volatile [link boost_typetraits.reference.is_volatile is_volatile]] [def __is_abstract [link boost_typetraits.reference.is_abstract is_abstract]] [def __is_polymorphic [link boost_typetraits.reference.is_polymorphic is_polymorphic]] diff --git a/include/boost/type_traits.hpp b/include/boost/type_traits.hpp index 9267a71..398c687 100644 --- a/include/boost/type_traits.hpp +++ b/include/boost/type_traits.hpp @@ -51,6 +51,7 @@ #include "boost/type_traits/is_const.hpp" #include "boost/type_traits/is_convertible.hpp" #include "boost/type_traits/is_copy_constructible.hpp" +#include "boost/type_traits/is_copy_assignable.hpp" #include "boost/type_traits/is_empty.hpp" #include "boost/type_traits/is_enum.hpp" #include "boost/type_traits/is_float.hpp" diff --git a/include/boost/type_traits/is_copy_assignable.hpp b/include/boost/type_traits/is_copy_assignable.hpp new file mode 100644 index 0000000..48af818 --- /dev/null +++ b/include/boost/type_traits/is_copy_assignable.hpp @@ -0,0 +1,147 @@ +// (C) Copyright Ion Gaztanaga 2014. +// +// Use, modification and distribution are 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/type_traits for most recent version including documentation. + +#ifndef BOOST_TT_IS_COPY_ASSIGNABLE_HPP_INCLUDED +#define BOOST_TT_IS_COPY_ASSIGNABLE_HPP_INCLUDED + +#include +#include +#include +#include + +#if !defined(BOOST_NO_CXX11_DELETED_FUNCTIONS) && !defined(BOOST_NO_CXX11_DECLTYPE) \ + && !defined(BOOST_INTEL_CXX_VERSION) && \ + !(defined(BOOST_MSVC) && _MSC_VER == 1800) +#define BOOST_TT_CXX11_IS_COPY_ASSIGNABLE +#include +#else + //For compilers without decltype + #include + #include + #include + #include +#endif + + +// should be the last #include +#include + +namespace boost { + +namespace detail{ + +template +struct is_copy_assignable_impl2 { + +// Intel compiler has problems with SFINAE for copy constructors and deleted functions: +// +// error: function *function_name* cannot be referenced -- it is a deleted function +// static boost::type_traits::yes_type test(T1&, decltype(T1(boost::declval()))* = 0); +// ^ +// +// MSVC 12.0 (Visual 2013) has problems when the copy constructor has been deleted. See: +// https://connect.microsoft.com/VisualStudio/feedback/details/800328/std-is-copy-constructible-is-broken +#if defined(BOOST_TT_CXX11_IS_COPY_ASSIGNABLE) + typedef boost::type_traits::yes_type yes_type; + typedef boost::type_traits::no_type no_type; + + template + static decltype(::boost::declval() = ::boost::declval(), yes_type() ) test(int); + + template + static no_type test(...); + + static const bool value = sizeof(test(0)) == sizeof(yes_type); + +#else + static BOOST_DEDUCED_TYPENAME boost::add_reference::type produce(); + + template + static boost::type_traits::no_type test(T1&, typename T1::boost_move_no_copy_constructor_or_assign* = 0); + + static boost::type_traits::yes_type test(...); + // If you see errors like this: + // + // `'T::operator=(const T&)' is private` + // `boost/type_traits/is_copy_assignable.hpp:NN:M: error: within this context` + // + // then you are trying to call that macro for a structure defined like that: + // + // struct T { + // ... + // private: + // T & operator=(const T &); + // ... + // }; + // + // To fix that you must modify your structure: + // + // // C++03 and C++11 version + // struct T: private boost::noncopyable { + // ... + // private: + // T & operator=(const T &); + // ... + // }; + // + // // C++11 version + // struct T { + // ... + // private: + // T& operator=(const T &) = delete; + // ... + // }; + BOOST_STATIC_CONSTANT(bool, value = ( + sizeof(test(produce())) == sizeof(boost::type_traits::yes_type) + )); + #endif +}; + +template +struct is_copy_assignable_impl2 { + BOOST_STATIC_CONSTANT(bool, value = false); +}; + +template +struct is_copy_assignable_impl { + +#if !defined(BOOST_TT_CXX11_IS_COPY_ASSIGNABLE) + //For compilers without decltype, at least return false on const types, arrays + //types derived from boost::noncopyable and types defined as BOOST_MOVEABLE_BUT_NOT_COPYABLE + typedef BOOST_DEDUCED_TYPENAME boost::remove_reference::type unreferenced_t; + BOOST_STATIC_CONSTANT(bool, value = ( + boost::detail::is_copy_assignable_impl2< + boost::is_base_and_derived::value + || boost::is_const::value || boost::is_array::value + ,T + >::value + )); + #else + BOOST_STATIC_CONSTANT(bool, value = ( + boost::detail::is_copy_assignable_impl2< + boost::is_base_and_derived::value,T + >::value + )); + #endif +}; + +} // namespace detail + +BOOST_TT_AUX_BOOL_TRAIT_DEF1(is_copy_assignable,T,::boost::detail::is_copy_assignable_impl::value) +BOOST_TT_AUX_BOOL_TRAIT_SPEC1(is_copy_assignable,void,false) +#ifndef BOOST_NO_CV_VOID_SPECIALIZATIONS +BOOST_TT_AUX_BOOL_TRAIT_SPEC1(is_copy_assignable,void const,false) +BOOST_TT_AUX_BOOL_TRAIT_SPEC1(is_copy_assignable,void const volatile,false) +BOOST_TT_AUX_BOOL_TRAIT_SPEC1(is_copy_assignable,void volatile,false) +#endif + +} // namespace boost + +#include + +#endif // BOOST_TT_IS_COPY_ASSIGNABLE_HPP_INCLUDED diff --git a/test/is_copy_assignable.cpp b/test/is_copy_assignable.cpp new file mode 100644 index 0000000..5171b32 --- /dev/null +++ b/test/is_copy_assignable.cpp @@ -0,0 +1,223 @@ +// (C) Copyright John Maddock 2000. +// (C) Copyright Ion Gaztanaga 2014. +// Use, modification and distribution are 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) +//#define TEST_STD +#include "test.hpp" +#include "check_integral_constant.hpp" + +#ifdef TEST_STD +# include +#else +# include +#endif + + +#include + +struct has { + has(){} + has &operator=(const has&){ return *this; } +}; + +// MSVC can not generate neither default constructor, nor assignment operator, +// nor copy constructor for `has2` type. Supressing those warnings is essential, +// because we treat warnings as errors in those tests +#if (defined _MSC_VER) +# pragma warning( push ) +# pragma warning( disable : 4510 4512 4610) +#endif +struct has2 { + int i; + has2 &operator=(const int& val) { i = val; return *this; } +}; +#if (defined _MSC_VER) +# pragma warning( pop ) +#endif + +struct has3 { // Copy assignment must be generated by compiler + has3(has3*){} +}; + +struct has_not: public boost::noncopyable { + typedef boost::noncopyable base_t; + has_not() : base_t() {} +}; + +#if defined(BOOST_TT_CXX11_IS_COPY_ASSIGNABLE) + +struct has_not2 { + has_not2() {} + has_not2& operator=(has_not2&) = delete; +}; + +struct has_not3 { + has_not3() {} + has_not3& operator=(const has_not3&) = delete; +}; + +#endif // BOOST_TT_CXX11_IS_COPY_ASSIGNABLE + +struct has_not4: private boost::noncopyable { + typedef boost::noncopyable base_t; + has_not4() : base_t() {} +private: + has_not4& operator=(const has_not4&); +}; + +struct has_not5 { +private: + BOOST_MOVABLE_BUT_NOT_COPYABLE(has_not5) +}; + +struct has_not6 { + has_not6& operator=(has_not6&){ return *this; } +}; + + +TT_TEST_BEGIN(is_copy_assignable) + +// Main part of the test +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, false); +#if defined(BOOST_TT_CXX11_IS_COPY_ASSIGNABLE) +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, false); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, false); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, false); +#endif +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, false); + +// Requires some basic support from Boost.Move in C++03 +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, false); + +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, false); + +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, false); + +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, false); + +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, false); + +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, false); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, false); + +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, false); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, false); + +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, false); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, false); + +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, false); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, false); + +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, false); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, false); + +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, false); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, false); + +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, false); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, false); + +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, false); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, false); + +#ifdef BOOST_HAS_LONG_LONG + +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable< ::boost::ulong_long_type>::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable< ::boost::ulong_long_type const>::value, false); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable< ::boost::long_long_type>::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable< ::boost::long_long_type const>::value, false); + +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable< ::boost::ulong_long_type volatile>::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable< ::boost::ulong_long_type const volatile>::value, false); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable< ::boost::long_long_type volatile>::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable< ::boost::long_long_type const volatile>::value, false); + +#endif + +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, false); + +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, false); + +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, false); + +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, false); + +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, false); + +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, false); + +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, false); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, true); + +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, false); + +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, false); +#endif + +// Following three tests may give different results because of compiler and C++03/C++11. +// On C++11 compiler following code: +// int c[2][4][5][6][3]; +// int b[2][4][5][6][3] = std::move(c); +// does not compile, so we expect `false` to be the result of those three tests. +BOOST_CHECK_SOFT_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, false, true); +BOOST_CHECK_SOFT_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, false, true); +BOOST_CHECK_SOFT_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, false, true); + +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, false); + +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, true); +BOOST_CHECK_INTEGRAL_CONSTANT(::tt::is_copy_assignable::value, true); + + +TT_TEST_END +