对于以下代码:
#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交换?这种情况的最佳做法是什么?
答案 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
相同的重载“强度”,并且我们会产生歧义。
我不知道如何创建这样一个不会与这个陷阱发生冲突的交换基础。
可能概念将为我们提供使朋友交换“比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
类旨在用于传统的多态性,则此解决方案将不起作用。