Opened 5 years ago

Closed 5 years ago

#9338 closed Bugs (fixed)

VS2005 compiler errors in swap() definition after including container/memory_util.hpp

Reported by: Fedor Trushkin <ted-xp@…> Owned by: Ion Gaztañaga
Milestone: To Be Determined Component: container
Version: Boost 1.54.0 Severity: Problem
Keywords: Cc:

Description

This problem is perfectly reproducable under VS2005 even with the latest boost 1_55 rc.

Here's the minimal reproducable example:

----- This was the original inclusion actually #include <boost/container/flat_map.hpp> #include <boost/container/detail/config_begin.hpp> #include <boost/intrusive/detail/memory_util.hpp> #include <boost/container/detail/memory_util.hpp>

void swap(); ------

In VS2005 this innocent piece of code yields: ------ error C2365: 'swap' : redefinition; previous definition was 'formerly unknown identifier' ------

VS2010 and GCC 4.5.2 have no complaints.

Looks like VS2005 compiler is entangled somehow by BOOST_INTRUSIVE_HAS_MEMBER magic. Actually, it's quite a headache that with VS2005 one can't introduce swap() after, say, flat_map is included.

Change History (6)

comment:1 Changed 5 years ago by Göran Orsander <goran.orsander.lw@…>

You get the same problem with VS2008 for both boost 1.54.0 and 1.55.0

I use <boost/container/string.hpp> and it will include both <container/detail/memory_util.hpp> and <intrusive/detail/memory_util.hpp>

Another third party library I use also defines 'swap' in the global namespace. This is quite a headache since I cannot modify the code in either boost or the other third party library.

comment:2 Changed 5 years ago by Ion Gaztañaga

Thanks for the report. I've tried to find a workaround with no luck. It's definitely a bug in the compiler, a really strange one. If your swap function is declared in a namespace the error disappears. The bug is present at least from Visual 2003.

The error is triggered from boost/intrusive/detail/has_member_function_callable_with.hpp, which produces (for swap) the code

template< class F

, std::size_t N = sizeof((boost::move_detail::declval<F>().swap (), 0))>

struct zeroarg_checker_swap

This ".swap()" call in the sizeof expression to use SFINAE to detect if the expression is correct seems to provoke the error. Sadly, I haven't found any workaround.

comment:3 Changed 5 years ago by Fedor Trushkin <ted-xp@…>

Could you explain, please, why zero arguments case is handled specially? I modified it to use the same ideas an in funwrap and it seems acting perfectly without any compiler errors. That's how it looks like after preprocessing:

         template<typename Fun>
         struct funwrap0_swap
            : Fun
         {
            funwrap0_swap();
            using Fun::swap;
            boost_intrusive_has_member_function_callable_with::private_type
               swap()  const;
         };
         template<typename Fun>
         struct has_member_function_callable_with_swap_impl
            <Fun, true
            , void
            >
         {
            typedef funwrap0_swap<Fun>
                     FunWrap;
            static bool const value =
            (sizeof(boost_intrusive_has_member_function_callable_with::no_type) ==
               sizeof(boost_intrusive_has_member_function_callable_with::is_private_type
                        (  (boost::move_detail::declval<FunWrap>().
                              swap(), 0
                           )
                        )
                     )
            );
         };

For me the only worrying thing is using std::swap statement, as I don't get how exactly should this be regarded by compiler if no swap is available in Fun base class.

comment:4 Changed 5 years ago by Ion Gaztañaga

If the template argument "Fun" has a member swap defined as:

struct Func
{
  void swap() const
  {}
};

Then the trait would lie and it would tell us that such member does not exist (the wrapper is a derived class that hides the base method). If you replace

boost_intrusive_has_member_function_callable_with::private_type swap() const;

with (variadic version)

boost_intrusive_has_member_function_callable_with::private_type swap(...) const;

then a compilation error would be triggered as

boost::move_detail::declval<FunWrap>().swap()

would be ambiguous (the compiler does not know if the variadic version from the wrapper or the base zero argument version should be called.

In non-zero argument cases, a special argument "dont_care" is used generated a very different member overload. If that is selected, then the member does not exist. With 0 arguments, this is not possible.

comment:5 Changed 5 years ago by Ion Gaztañaga

A workaround is to tweak "boost/intrusive/detail/has_member_function_callable_with.hpp" to support non-zero lower bound values. Until now, are traits are generated from 0 to N arguments, but many of them don't need 0 arguments. "swap" is one of them (we are looking for "T::swap(T&)", no "T::swap()"). This would also generate less preprocessed code.

Other member names are likely to be less used in the global namespace than "swap", so let's see if this can at least minimize the compiler bug effects.

comment:6 Changed 5 years ago by Ion Gaztañaga

Resolution: fixed
Status: newclosed

(In [86748]) Fixes #9338

Note: See TracTickets for help on using tickets.