On Fri, Apr 27, 2012 at 2:09 PM, Jonathan Jones <Jonathan.Jones@mathworks.com> wrote:
On Apr 27, 2012, at 4:56 PM, Jeffrey Lee Hellrung, Jr. wrote:
On Fri, Apr 27, 2012 at 12:47 PM, Jonathan Jones <Jonathan.Jones@mathworks.com> wrote:
Hello,
The following code fails to compile when using GCC 4.4.6 and boost::optional from Boost 1.49:
Hmmm...seems to build fine for me with MSVC9 (also Boost 1.49.0)
The problem seems specific to GCC. It compiles fine for me with MSVC 9 and clang 2.1. I'm guessing this is in part due to the fact that with both those compilers, boost::is_convertible is implemented using compiler intrinsics, whereas this is not the case with GCC.
#include <boost/optional/optional.hpp>
#include <boost/type_traits/is_convertible.hpp>
struct grill
{
template<typename U>
grill (U)
{}
};
template<typename T>
struct bar
{
template<typename U>
bar (U,
typename boost::is_convertible<U,T>::type* = 0)
{}
};
void foo()
{
typedef bar<grill> data_type;
boost::optional<data_type> opt;
boost::optional<data_type> opt2(opt); // this line
(void)opt2;
}
Here are the error messages (I apologize for the long lines, I shortened them as much as possible by truncating the header paths):
type_traits/is_convertible.hpp: In instantiation of ‘const bool boost::detail::is_convertible_basic_impl<boost::optional_detail::optional_base<bar<grill> >&, grill>::value’:
type_traits/is_convertible.hpp:329: instantiated from ‘const bool boost::detail::is_convertible_impl<boost::optional_detail::optional_base<bar<grill> >, grill>::value’
type_traits/is_convertible.hpp:452: instantiated from ‘boost::is_convertible<boost::optional_detail::optional_base<bar<grill> >, grill>’
optional/optional.hpp:604: instantiated from ‘boost::optional<T>::optional(const boost::optional<T>&) [with T = bar<grill>]’
optional.cpp:25: instantiated from here
optional/optional.hpp:285: error: ‘boost::optional_detail::optional_base<T>::optional_base(const boost::optional_detail::optional_base<T>&) [with T = bar<grill>]’ is protected
type_traits/is_convertible.hpp:170: error: within this context
type_traits/is_convertible.hpp:170: error: initializing argument 1 of ‘grill::grill(U) [with U = boost::optional_detail::optional_base<bar<grill> >]’
optional/optional.hpp:308: error: ‘boost::optional_detail::optional_base<T>::~optional_base() [with T = bar<grill>]’ is protected
type_traits/is_convertible.hpp:170: error: within this context
This reproduction case is distilled from a real world example (not my code) where bar=boost::fusion::vector1 and grill=boost::function<void()>
The code compiles fine using the same compiler and Boost 1.44.
Any ideas on the root of the problem?
I can't figure it out from a quick glance through optional.hpp :( Can you investigate what has changed in optional.hpp between 1.44 and 1.49?
I suspect the "breaking" change is this one: https://svn.boost.org/trac/boost/changeset/67183
Note that on lines 560 and 594, a call to static_cast was added for copy construction and assignment. If I remove the static_cast, things start to compile again, but there isn't enough info in the change set as to why the static_casts were added.
I see. gcc must be investigating the viability of the optional_base::optional_base(argument_type) and optional_base::assign(argument_type) overloads (even though the corresponding overloads taking an optional_base const & argument are clearly preferable), and failing when seeing whether optional_base is convertible to grill (indirectly from determining whether it is convertible to bar<grill>), since this requires access to optional_base's copy constructor since grill has a by-value converting constructor.
I have a hard time faulting the optional code, because sometimes you do need to explicitly cast to the base type from the derived class to ensure the right base class overload is used. I would be inclined to view an unconstrained implicit conversion constructors like that exhibited in grill suspiciously, but I can see its virtue within boost::fuction. I don't know. Perhaps boost::function's constructor should be by reference? Or maybe the static_cast's should be reverted? Or maybe optional_base::optional_base(argument_type) should be changed to template< class U > optional_base::optional_base(U&, typename enable_if_c< is_same< U&, argument_type >::value >::type* = 0) ? I feel like there is a correct thing to do here but it's escaping me :(
- Jeff