实现扩展的内省交换算法

时间:2016-01-23 21:09:34

标签: c++ boost c++14 swap argument-dependent-lookup

我知道ADL和交换习语:

using std::swap;
swap(x, y);

boost::swap()为您完成上述操作。现在,我想进一步推动它。具体来说,如果可能,让交换执行x.swap(y),否则回退到boost::swap()。因此,您不必同时实现成员交换和免费交换,这是冗长和冗余的。我试图实现这样的交换,最后得到以下结果。实现这样的事情会变得非常棘手。所以,我想知道我的实现是否存在任何缺陷,或者是否存在更简洁的实现。

#include <algorithm>
#include <utility>

namespace cppu_detail_swap {

template <typename T>
void swap_impl(T& x, T& y) {
  using std::swap;
  swap(x, y);
}

} // namespace cppu_detail_swap

namespace cppu {

namespace detail {

template <typename T>
void swap(T& x, T& y, int) {
  cppu_detail_swap::swap_impl(x, y);
}

template <typename T>
auto swap(T& x, T& y, char) -> decltype(x.swap(y)) {
  return x.swap(y);
}

} // namespace detail

template <typename T>
void swap(T& x, T& y) {
  detail::swap(x, y, ' ');
}

} // namespace cppu

1 个答案:

答案 0 :(得分:1)

您当前的解决方案对于来自cppu名称空间的对象存在缺陷,例如

// [insert your code here]

namespace cppu
{
  struct X{};
  struct Y{ void swap(Y& y) { }; };
}


int main()
{
  auto x1 = cppu::X{};
  auto x2 = cppu::X{};
  swap(x1, x2);

  auto y1 = cppu::Y{};
  auto y2 = cppu::Y{};
  swap(y1, y2);
}
g ++告诉我:

  

taste.cpp:9:7:错误:调用重载'swap(cppu :: X&amp;,cppu :: X&amp;)'是不明确的

要摆脱这种情况,你需要在std::swap中显式调用swap_impl,这是好的,因为你已经通过cppu :: swap实现到达这里了。但是,您不要将重载用于其他类型。因此,我认为您需要区分三种情况:

  1. 拥有自己的互换会员功能
  2. 没有交换成员函数且来自命名空间cppu
  3. 没有交换成员函数,并且是任何其他命名空间(在这里您需要使用ADL交换习惯用法)。
  4. 另外,我同意@Yakk我会更直接而不是使用int / char hack。

    让我们去吧:

    检查交换成员可用性的帮助程序:

    namespace cppu
    {
      namespace detail
      { 
        template <typename T>
        using void_t = void;
    
        template <typename T, typename = void>
        struct has_member_swap
        {
          static constexpr bool value = false;
        };
    
        template <typename T>
        struct has_member_swap<
            T,
            void_t<decltype(std::declval<T&>().swap(std::declval<T&>()))>>
        {
          static constexpr bool value = true;
        };
      }
    }
    

    帮助检查T是否来自命名空间cppu,另请参阅here

    namespace helper
    {
      template <typename T, typename = void>
      struct is_member_of_cppu : std::false_type
      {
      };
    
      template <typename T>
      struct is_member_of_cppu<
          T,
          decltype(adl_is_member_of_cppu(std::declval<T>()))> : std::true_type
      {
      };
    }   
    
    namespace cppu
    {   
      template <typename T>
      auto adl_is_member_of_cppu(T && ) -> void;
    }
    

    现在我们可以编写所有三个重载:

    namespace cppu
    {
      namespace detail
      {
        template <
            typename T,
            typename = std::enable_if_t<helper::is_member_of_cppu<T>::value and
                                        not has_member_swap<T>::value>>
        auto swap(T& x, T& y) 
            -> std::enable_if_t<helper::is_member_of_cppu<T>::value and
                                not has_member_swap<T>::value>
        {
          std::cout << "cppu-type without member swap";
          std::swap(x, y);
        }
    
        template <
            typename T,
            typename = std::enable_if_t<not helper::is_member_of_cppu<T>::value and
                                        not has_member_swap<T>::value>>
        auto swap(T& x, T& y)
            -> std::enable_if_t<not helper::is_member_of_cppu<T>::value and
                                not has_member_swap<T>::value>
        {
          std::cout << "not cppu-type without member swap";
          using std::swap;
          swap(x, y);
        }
    
        template <typename T, typename = std::enable_if_t<has_member_swap<T>::value>>
        auto swap(T& x, T& y) -> decltype(x.swap(y))
        {
          std::cout << "member swap";
          return x.swap(y);
        }
    
      }
    }
    

    像以前一样调用此方法:

    namespace cppu
    {
      template <typename T>
      void swap(T& x, T& y)
      {
        detail::swap(x, y);
      }   
    }
    

    最后:测试整个事情。

    namespace cppu
    {
      struct X{};
      struct Y{ void swap(Y& y) { }; };
    }
    
    struct A{};
    struct B{ void swap(B& y) { }; };
    struct C{};
    auto swap(C&, C&) -> void { std::cout << " with own overload"; }
    
    static_assert(helper::is_member_of_cppu<cppu::X>::value, "");
    static_assert(helper::is_member_of_cppu<cppu::Y>::value, "");
    static_assert(not helper::is_member_of_cppu<A>::value, "");
    static_assert(not helper::is_member_of_cppu<B>::value, "");
    
    
    int main()
    {
      auto x1 = cppu::X{};
      auto x2 = cppu::X{};
      std::cout << "X: "; swap(x1, x2); std::cout << std::endl;
    
      auto y1 = cppu::Y{};
      auto y2 = cppu::Y{};
      std::cout << "Y: "; swap(y1, y2); std::cout << std::endl;
    
      auto a1 = A{};
      auto a2 = A{};
      std::cout << "A: "; cppu::swap(a1, a2); std::cout << std::endl;
    
      auto b1 = B{};
      auto b2 = B{};
      std::cout << "B: "; cppu::swap(b1, b2); std::cout << std::endl;
    
      auto c1 = C{};
      auto c2 = C{};
      std::cout << "C: "; cppu::swap(c1, c2); std::cout << std::endl;
    }
    

    输出符合预期(恕我直言):

      

    X:没有成员交换的cppu-type   Y:会员互换   答:不是没有成员交换的cppu类型   B:会员互换   C:没有cppu-type没有成员交换自己的重载

相关问题