朋友 class 在 C++ 中的非常量模板化版本
Friend the non-const templated version of class in C++
考虑以下代码:
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>"
}
}
不管这是一个非常人为的例子,访问 class 版本的私有数据成员的正确方法是什么,仅与 T
的常量不同?如果情况相反,我可以简单地做:friend class Me<T const>;
我不想在我的真实代码中公开一种访问指向更广阔世界的内部数据指针的方法(即使通过 getter)。另外,我在这里没有做任何违反 const 正确性的事情,因为我永远不会修改等式的右边。
也许这完全是错误的做法,但在当时看来是一件非常合理的事情。
创建一个免费功能会更好吗?例如
template<class T>
Me<T> operator= (const Me<T>& lhs, const Me<T const>& rhs) {...}
第二种方法编译得很好,但给了我一个 link(未定义的符号)错误,我仍在追查。
right way to access a private data member of a version of the class differing only by constness of T?
怎么样
friend class Me<std::remove_const_t<T>>;
?
在您的情况下,T
(无 const
)版本必须访问 T const
版本的私有数据。
所以你必须交朋友T
(没有const
)版本。
题外话建议:你标记了C++17,所以你可以使用智能指针。
使用它们!
默认的复制构造函数和 Me
的默认 operator=()
非常非常危险(几乎可以保证双重释放相同分配的内存)。
您需要定义 class 的赋值运算符,并且在 Me<T const>
上工作的模板化运算符也为非 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;
我认为这比接受的答案稍微好一些,因为它不需要重载赋值运算符。此外,此版本将禁用 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;
}
考虑以下代码:
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>"
}
}
不管这是一个非常人为的例子,访问 class 版本的私有数据成员的正确方法是什么,仅与 T
的常量不同?如果情况相反,我可以简单地做:friend class Me<T const>;
我不想在我的真实代码中公开一种访问指向更广阔世界的内部数据指针的方法(即使通过 getter)。另外,我在这里没有做任何违反 const 正确性的事情,因为我永远不会修改等式的右边。
也许这完全是错误的做法,但在当时看来是一件非常合理的事情。
创建一个免费功能会更好吗?例如
template<class T>
Me<T> operator= (const Me<T>& lhs, const Me<T const>& rhs) {...}
第二种方法编译得很好,但给了我一个 link(未定义的符号)错误,我仍在追查。
right way to access a private data member of a version of the class differing only by constness of T?
怎么样
friend class Me<std::remove_const_t<T>>;
?
在您的情况下,T
(无 const
)版本必须访问 T const
版本的私有数据。
所以你必须交朋友T
(没有const
)版本。
题外话建议:你标记了C++17,所以你可以使用智能指针。
使用它们!
默认的复制构造函数和 Me
的默认 operator=()
非常非常危险(几乎可以保证双重释放相同分配的内存)。
您需要定义 class 的赋值运算符,并且在 Me<T const>
上工作的模板化运算符也为非 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;
我认为这比接受的答案稍微好一些,因为它不需要重载赋值运算符。此外,此版本将禁用 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;
}