使用基类的ADL交换

时间:2017-01-20 01:04:30

标签: c++ c++11

对于以下代码:

#include <iostream>
#include <utility>

struct Base {
    friend void swap(Base&, Base&)
    {
        std::cout << "base swap\n";
    }
};

struct Derived : Base {};

int main()
{
    Derived d1, d2;

    using std::swap;
    swap(d1, d2);
}

当我尝试在Derived个对象上使用ADL交换时,不会调用Base的交换函数。而是使用std::swap。我是否必须为Derived编写单独的ADL交换?这种情况的最佳做法是什么?

3 个答案:

答案 0 :(得分:2)

考虑基数为swap,但std::swap被认为是更好的匹配,因为它不需要转换为基数。

这是一个刺痛:

struct Base {
  template<class T,
    std::enable_if_t<std::is_base_of<Base, T>{}, int> =0
  >
  friend void swap(T&, T&) {
    std::cout << "base swap\n";
  }
};

可悲的是,这不起作用,因为它具有完全std::swap相同的重载“强度”,并且我们会产生歧义。

live example

我不知道如何创建这样一个不会与这个陷阱发生冲突的交换基础。

可能概念将为我们提供使朋友交换“比std::swap更专业”所需的力量。

答案 1 :(得分:1)

正如 Yakk 所说,没有办法制作比std::swap更专业的功能。但我们可以自己制作。

#include <iostream>
#include <utility>
#include <type_traits>

struct Base {
    template<
        class T,
        std::enable_if_t<std::is_base_of<Base, T>{}, int> = 0
    >
    friend void adl_swap(T& lhs, T& rhs) {
        std::cout << "base swap\n";
    }
};

struct Derived : Base {};


template<
    class T,
    std::enable_if_t<!std::is_base_of<Base, T>{}, int> = 0
>
void adl_swap(T& lhs, T& rhs) {
    std::cout << "std::swap\n";
    using std::swap;
    swap(lhs, rhs);
}


int main() {
    Derived d1, d2;
    adl_swap(d1, d2); // output : base swap

    int a{1}, b{2};
    adl_swap(a,b); // output : std::swap
}

答案 2 :(得分:1)

为基类中的派生类型提供swap函数的一种方法是使用CRTP模式将上述类型转发给以后的类型。

#include <iostream>

template<typename T>
struct Base {
    friend void swap(Base&, Base&) {
        std::cout << "base swap" << std::endl;
    }
    friend void swap(T& lhs, T& rhs) {  // Preferred over std::swap
        swap(static_cast<Base&>(lhs), static_cast<Base&>(rhs));
    }
};

struct Derived : Base<Derived> {};

int main() {
    Derived d1, d2;

    using std::swap;
    swap(d1, d2);
}

之所以行之有效,是因为函数的直接重载比模板化的重载要好,如 Yakk 在其答复中所述。

如果该Base类旨在用于传统的多态性,则此解决方案将不起作用。