2

I am looking into boost::swap implementation:

namespace boost_swap_impl
{
  template<class T>
  BOOST_GPU_ENABLED
  void swap_impl(T& left, T& right)
  {
    using namespace std;//use std::swap if argument dependent lookup fails
    swap(left,right);
  }

  template<class T, std::size_t N>
  BOOST_GPU_ENABLED
  void swap_impl(T (& left)[N], T (& right)[N])
  {
    for (std::size_t i = 0; i < N; ++i)
    {
      ::boost_swap_impl::swap_impl(left[i], right[i]);
    }
  }
}

namespace boost
{
  template<class T1, class T2>
  BOOST_GPU_ENABLED
  void swap(T1& left, T2& right)
  {
    ::boost_swap_impl::swap_impl(left, right);
  }
}

The implementation also contains the following comment:

// Note: the implementation of this utility contains various workarounds:
// - swap_impl is put outside the boost namespace, to avoid infinite
// recursion (causing stack overflow) when swapping objects of a primitive
// type.

However, I don't understand why primitive types (and why only primitive) cause infinite recursion.

Anton Frolov
  • 291
  • 4
  • 15
  • The namespace is most likely an ADL barrier. But I'm not at the moment in the mood/don't have the time to further explain – sehe May 04 '15 at 14:18
  • @Puppy, boost reimplements it so you don't need to write using std::swap before swap(a,b) to fall back to std::swap each time you fail to get more effective implementation via ADL. – Anton Frolov May 04 '15 at 14:45
  • @AntonFrolov The real question is why `std::swap` is not implemented this way. – Lingxi May 04 '15 at 15:26

1 Answers1

2

If swap_impl is in the namespace boost, the call swap(left,right); in the implementation of swap_impl will resolve to boost::swap instead of std::swap. That is, boost::swap -> boost::swap_impl -> boost::swap, and thus infinite recursion.

As dyp has pointed out in the comment, the correct interpretation of the comment //use std::swap if argument dependent lookup fails should be as follows: An unqualified swap(left,right) is intended to select a specialized swapping function for the two arguments, found in the namespace of the types of those arguments. If such a specialized function has not been provided, the generic std::swap is used as a fallback.

Lingxi
  • 14,579
  • 2
  • 37
  • 93
  • This was my initial idea, however, there are two things that confuse me: 1) why infinite recursion happens with primitive types only; 2) why it resolves to boost::swap? the compiler should see swap implementation in both ::std and ::boost and issue an error due to ambiguous call. – Anton Frolov May 04 '15 at 13:54
  • 1) I guess the writer is really trying to mean types that haven't overloaded `std::swap`. Primitive types are among them. 2) This is governed by the function call resolution rules. I'm not familiar with the details of the rules, and thus unable to give you an exact answer about this. Sorry. – Lingxi May 04 '15 at 14:00
  • http://stackoverflow.com/questions/30032332/confustions-around-function-call-resolution. This question should help us both :-) – Lingxi May 04 '15 at 14:14
  • @dyp Just enlightened by your comment. This interpretation of the comment should be exact. – Lingxi May 04 '15 at 14:29