模板化特殊成员的重载解析(运算符=)

overload resolution of templated special member (operator=)

本文关键字:运算符 重载 成员      更新时间:2023-10-16

>上下文:尝试在模板化的自制shared_ptr上实现复制和交换习语。我无法调用此函数:

template<typename U>
shared_ptr<T>& operator=(shared_ptr<U> rhs)
{
static_assert(std::is_base_of<T, U>::value);
swap(*this, rhs);
return *this;
}

从不必要的代码中剥离:

以下代码无法编译。"x=y"行是尝试使用:

foo<T>& operator=(const foo<U>& ptr) = delete;

如果我删除/评论已删除的功能行。这意味着如果我不定义它们,编译器会为我做,代码会编译。但是,编译器生成的特殊成员将优先于我的运算符,该运算符不会被调用。

template<typename T> 
class foo
{
public:
// copy&swap
template<typename U>
foo<T>& operator=(foo<U> rhs)
{
// whatever is needed swap and all
return *this;
}
template<typename U>
foo<T>& operator=(const foo<U>& ptr) = delete;
template<typename U>
foo<T>& operator=(foo<U>&& ptr) = delete;
foo<T>& operator=(foo<T>&& ptr) = delete;
};
int main()
{
foo<int> x,y;
x = y;
return 0;
}

问题

  • 我不清楚这里的重载分辨率。 为什么一个签名比另一个签名更受欢迎?
  • 如果删除函数,我知道这不是"替换失败",但它仍然是签名级别的失败,而不是在正文内部,为什么编译器停止寻找匹配?
  • 有什么解决方案可以调用函数吗?


编辑/回答

这是到目前为止的正确版本:

  • 非模板构造函数(移动/复制(被标记为显式,以便将 foo 分配给 foo 调用模板版本。
  • 添加了适当的静态断言,因此转换 U/T 有意义
  • 参数完美地转发到分配/移动内部函数

法典:

#include <iostream>
template<typename T>
class foo
{
private:
template <typename U> friend class foo;
template<typename U>
void assign(const foo<U>& other)
{
static_assert(std::is_base_of<T, U>::value || std::is_convertible<U, T>::value);
std::cout << "templated assign function" << std::endl;
this->data = other.data;
}
template<typename U>
void move(foo<U>&& other)
{
static_assert(std::is_base_of<T, U>::value || std::is_convertible<U, T>::value);
std::cout << "templated move function" << std::endl;
this->swap(*this, other);
}
public:
template<class X, class Y> void swap(foo<X>& left, foo<Y>& right) noexcept
{
std::cout << "templated swap function" << std::endl;
std::swap(left.data, right.data);
}
void swap(foo<T>& left, foo<T>& right) noexcept
{
std::cout << "swap function" << std::endl;
std::swap(left.data, right.data);
}
foo() {}
explicit foo(foo<T>&& other)
{
std::cout << "move constructor foo(foo&& other)" << std::endl;
move(std::forward<decltype(other)>(other));
}

explicit foo(const foo<T>& other)
{
std::cout << "copy constructor foo(const foo& other)" << std::endl;
assign(std::forward<decltype(other)>(other));
}
template<typename U>
foo(foo<U>&& other)
{
static_assert(std::is_base_of<T, U>::value || std::is_convertible<U, T>::value);
std::cout << "templated move constructor template<typename U> foo(foo<U>&& other)" << std::endl;
move(std::forward<decltype(other)>(other));
}
template<typename U>
foo(const foo<U>& other)
{
static_assert(std::is_base_of<T, U>::value || std::is_convertible<U,T>::value);
std::cout << "templated copy constructor template<typename U> foo(const foo<U>& other)" << std::endl;
assign(std::forward<decltype(other)>(other));
}
// copy&swap
template<typename U>
foo<T>& operator=(foo<U> rhs)
{
static_assert(std::is_base_of<T, U>::value || std::is_convertible<U, T>::value);
std::cout << "templated assignement template<typename U> foo<T>& operator=(foo<U> rhs)" << std::endl;
auto tmp = rhs.data;
rhs.data = reinterpret_cast<U*>(data);
data = reinterpret_cast<T*>(tmp);
return *this;
}
foo<T>& operator=(foo<T> rhs)
{
std::cout << "assignement foo<T>& operator=(foo<T> rhs)" << std::endl;
std::swap(rhs.data,data);
return *this;
}
private:
T * data;
};
int main()
{
foo<int> x,y;
const foo<int>& cy = y;
foo<short> z, w;
x = y;
x = cy;
x = std::move(y);
x = z;
x = std::move(w);
return 0;
}
  1. operator=(const foo&)仅在 cv 限定符上有所不同,并且将首选模板
  2. = delete会导致禁止operator=(const foo&)。它不仅阻止编译器生成它。

这应该这样做:

#include <tuple>
#include <iostream>
template<typename T>
class foo
{
private:
template <typename U> friend class foo;
foo& assign(T rhs)
{
std::swap(data, rhs);
return *this;
}
public:

template<typename U>
foo& operator=(const foo<U>& rhs)
{
return assign(rhs.data);
}
template<typename U>
foo& operator=(foo<U>&& rhs)
{
return assign(std::move(rhs.data));
}
foo& operator=(const foo& rhs)
{
return assign(rhs.data);
}
foo& operator=(foo&& rhs)
{
return assign(std::move(rhs.data));
}
private:
T data;
};
int main()
{
foo<int> x,y;
const foo<int>& cy = y;
foo<short> z, w;
x = y;
x = cy;
x = std::move(y);
x = z;
x = std::move(w);
return 0;
}

编辑1:添加成员并调用交换。

Edit2:将分配更改为按值获取T,而不是foo<T>添加移动分配

注意:现在,在分配不同类型的foo时,这会使用T的转换构造函数 (U(