模板化特殊成员的重载解析(运算符=)
overload resolution of templated special member (operator=)
>上下文:尝试在模板化的自制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;
}
operator=(const foo&)
仅在 cv 限定符上有所不同,并且将首选模板= 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
(
相关文章:
- 使用C++中的模板和运算符重载执行矩阵运算
- 为什么这个运算符<重载函数对 STL 算法不可见?
- <T> 通过模板化运算符重载将 std::complex 乘以双倍
- C++20概念:需要运算符重载
- 使用赋值运算符重载从类中返回jobject
- 在运算符重载定义中使用成员函数(const错误)
- 字节到位运算符重载C++
- 为什么在运算符重载时需要参考?
- 类中 c++ 的运算符 + 重载
- 算术复合运算符重载为非成员
- 运算符重载 (+),用于添加两个具有 C++ 的数组
- 交换运算符 + 重载会导致无限递归
- 如何理解新的运算符重载?
- 向量保持复数的运算符重载
- 如何创建运算符重载?
- 链接列表运算符重载没有打印出我想要的内容
- C++:需要帮助了解运算符重载错误
- 使用模板化运算符重载 XOR 运算符失败
- 如何确保接受的C++模板类型使运算符重载?
- 运算符重载使用运算符+添加类模板