请考虑以下代码:
template<class T>
class Me {
private:
T* data;
size_t size;
friend class Me<?????>;
public:
Me(size_t s) : data(new T[s]), size(s) {}
virtual ~Me() { delete [] data; }
Me<T> operator= (const Me<T const>& rhs) {
for(size_t i = 0; i < size; ++i)
data[i] = rhs.data[i]; // Gives compile error "data is a private member of Me<XX const>"
}
}
不管这是一个非常人为的示例,访问类的版本(仅由T
来区别)的私有数据成员的正确方法是什么?如果是相反的话,我可以简单地做:friend class Me<T const>;
我不想在我的真实代码中公开一种访问内部数据指针的方法(甚至通过getter)。另外,在这里我不会做任何违反const正确性的事情,因为我永远不会修改等式的右边。
也许这完全是错误的做法,但是在当时看来这是一件非常合理的事情。
创建一个自由函数会更好吗?例如
template<class T>
Me<T> operator= (const Me<T>& lhs, const Me<T const>& rhs) {...}
第二种方法可以很好地编译,但仍给我一个链接错误(未定义符号)。
答案 0 :(得分:4)
您需要定义该类的赋值运算符,并且可以在Me<T const>
上使用的模板化赋值运算符还可以将no const版本赋予友谊:
template<class T>
class Me {
private:
friend class Me<std::remove_const_t<T>>;
public:
Me<T>& operator= (const Me<T>& rhs) {
// ...
return *this;
}
template <class U, std::enable_if_t<std::is_same_v<U, T const>, bool> = true>
Me<T>& operator=(Me<U> const& rhs) {
// ...
return *this;
}
};
Me<int> r;
Me<int const> l;
r = l;
答案 1 :(得分:3)
我认为这比公认的答案略好,因为它不需要重载赋值运算符。另外,此版本将禁用const T
类型的赋值运算符。 (因为您可能始终不想启用它们)。
#include <type_traits>
template<class T>
class Me {
private:
friend class Me<std::remove_const_t<T>>;
public:
template <class U>
std::enable_if_t<std::is_same<std::remove_const_t<U>, T>::value, Me<T>&>
operator=(Me<U> const& rhs) {
return *this;
}
};
int main() {
Me<int> r;
Me<int const> l;
r = l;
r = r;
l = l;
l = r; //will cause compilation failure.
}
此版本还将支持const T
类型的赋值运算符。
template <class U>
std::enable_if_t<std::is_same<std::remove_const_t<U>, std::remove_const_t<T>>::value, Me<T>&>
operator=(Me<U> const& rhs) {
return *this;
}
答案 2 :(得分:1)
访问类的版本的私有数据成员的正确方法,仅区别于T的常量性?
那
friend class Me<std::remove_const_t<T>>;
?
您要使用的T
版本(无const
)必须访问T const
版本的私有数据。
因此,您必须成为T
(没有const
)版本的朋友。
主题建议:您标记了C ++ 17,因此可以使用智能指针。
使用它们!
默认的复制构造函数和operator=()
中的默认Me
非常非常危险(几乎没有保证双重分配的相同分配内存)。