无法访问模板化重载运算符中的私有成员
Can't access private member in templated overloaded operator
在这段代码中,为什么无法在运算符重载中访问我的 class 的私有字段?
(注意这只是一个MRE,不是完整代码)
template <typename T>
class Frac
template <typename T, typename U>
Frac<T> operator+ (Frac<T> lhs,const Frac<U>& rhs);
template <typename T, typename U>
bool operator==(const Frac<T>& lhs, const Frac<U>& rhs);
template <typename T>
class Frac {
template<typename>
friend class Frac;
friend Frac operator+ <>(Frac lhs,const Frac& rhs);
friend bool operator== <>(const Frac& lhs, const Frac& rhs);
private:
T numerator, denominator;
};
template <typename T, typename U>
Frac<T> operator+(Frac<T> lhs,const Frac<U>& rhs) {
lhs.denominator += rhs.denominator;
return lhs;
}
template <typename T, typename U>
bool operator==(const Frac<T>& lhs, const Frac<U>& rhs) {
return (lhs.numerator == rhs.numerator && lhs.denominator == rhs.denominator);
}
当我编译时,编译器告诉我无法访问分母和分子字段,因为它们是私有的。但是,过载显示为友好。 class 也被指示为友好的,因此 class 的所有实例无论类型如何都是友好的。
有人可以向我解释问题是什么以及如何解决吗?
制作
的每个实例
template <typename T, typename U>
bool operator==(const Frac<T>& lhs, const Frac<U>& rhs);
朋友,您的 friend
声明需要同样冗长。复制此声明并在其中粘贴“friend
”。有两个怪癖。首先,template
必须在 friend
之前,因此您将在声明的中间添加关键字。其次,T
已被用作 class 的模板参数,因此您应该选择不同的标识符以避免隐藏(我将使用 S
)。
template <typename S, typename U>
// ^^^
friend bool operator==(const Frac<S>& lhs, const Frac<U>& rhs);
// ^^^^^^ ^^^
如果不做这个改变,你是说 Frac<T>
的朋友是一个带有两个 Frac<T>
参数的运算符(与 T
相同)。
it is not possible to access the denominator and numerator fields because they are private.
是的,您还没有制作免费功能friend
。您已经创建了 classes friend
s,但这对免费功能没有帮助。一种更简单的解决方案是在 class 定义中定义它们。
示例:
template <typename U>
friend Frac operator+(Frac lhs, const Frac<U>& rhs) {
lhs.denominator += rhs.denominator;
return lhs;
}
但是,如果您将 operator+=
设为成员函数,则 operator+
可以在没有任何 friend
ship 的情况下实现为自由函数。所有 Frac<>
之间的 friend
ship 已经建立,因此不需要额外的 friend
声明。
示例:
#include <iostream>
#include <utility>
template <typename T>
class Frac {
public:
template <typename> // be friends with all Frac's
friend class Frac;
Frac() = default; // needed because for the templated converting ctor below
// a converting constructor from any Frac:
template<class U>
explicit Frac(const Frac<U>& rhs) :
numerator(rhs.numerator), denominator(rhs.denominator) {}
template <typename U>
Frac& operator+=(const Frac<U>& rhs) {
denominator += rhs.denominator; // ok: rhs has befriended Frac<T>
return *this;
}
template <typename U>
bool operator==(const Frac<U>& rhs) const {
// ok: rhs has befriended Frac<T> here too
return numerator == rhs.numerator && denominator == rhs.denominator;
}
private:
T numerator{}, denominator{};
};
// This free function doesn't need to be a friend. It uses the member function
// operator+=
// The returned type, Fact<R>, is deduced by fetching the type you'd gotten
// if you add a T and U.
template<typename T, typename U,
typename R = decltype(std::declval<T>() + std::declval<U>())>
Frac<R> operator+(const Frac<T>& lhs, const Frac<U>& rhs) {
Frac<R> rv(lhs); // use the converting constructor
rv += rhs;
return rv;
}
int main() {
Frac<int> foo;
Frac<double> bar;
auto r = foo + bar; // r is a Frac<double> (int + double => double)
}
在这段代码中,为什么无法在运算符重载中访问我的 class 的私有字段?
(注意这只是一个MRE,不是完整代码)
template <typename T>
class Frac
template <typename T, typename U>
Frac<T> operator+ (Frac<T> lhs,const Frac<U>& rhs);
template <typename T, typename U>
bool operator==(const Frac<T>& lhs, const Frac<U>& rhs);
template <typename T>
class Frac {
template<typename>
friend class Frac;
friend Frac operator+ <>(Frac lhs,const Frac& rhs);
friend bool operator== <>(const Frac& lhs, const Frac& rhs);
private:
T numerator, denominator;
};
template <typename T, typename U>
Frac<T> operator+(Frac<T> lhs,const Frac<U>& rhs) {
lhs.denominator += rhs.denominator;
return lhs;
}
template <typename T, typename U>
bool operator==(const Frac<T>& lhs, const Frac<U>& rhs) {
return (lhs.numerator == rhs.numerator && lhs.denominator == rhs.denominator);
}
当我编译时,编译器告诉我无法访问分母和分子字段,因为它们是私有的。但是,过载显示为友好。 class 也被指示为友好的,因此 class 的所有实例无论类型如何都是友好的。
有人可以向我解释问题是什么以及如何解决吗?
制作
的每个实例template <typename T, typename U>
bool operator==(const Frac<T>& lhs, const Frac<U>& rhs);
朋友,您的 friend
声明需要同样冗长。复制此声明并在其中粘贴“friend
”。有两个怪癖。首先,template
必须在 friend
之前,因此您将在声明的中间添加关键字。其次,T
已被用作 class 的模板参数,因此您应该选择不同的标识符以避免隐藏(我将使用 S
)。
template <typename S, typename U>
// ^^^
friend bool operator==(const Frac<S>& lhs, const Frac<U>& rhs);
// ^^^^^^ ^^^
如果不做这个改变,你是说 Frac<T>
的朋友是一个带有两个 Frac<T>
参数的运算符(与 T
相同)。
it is not possible to access the denominator and numerator fields because they are private.
是的,您还没有制作免费功能friend
。您已经创建了 classes friend
s,但这对免费功能没有帮助。一种更简单的解决方案是在 class 定义中定义它们。
示例:
template <typename U>
friend Frac operator+(Frac lhs, const Frac<U>& rhs) {
lhs.denominator += rhs.denominator;
return lhs;
}
但是,如果您将 operator+=
设为成员函数,则 operator+
可以在没有任何 friend
ship 的情况下实现为自由函数。所有 Frac<>
之间的 friend
ship 已经建立,因此不需要额外的 friend
声明。
示例:
#include <iostream>
#include <utility>
template <typename T>
class Frac {
public:
template <typename> // be friends with all Frac's
friend class Frac;
Frac() = default; // needed because for the templated converting ctor below
// a converting constructor from any Frac:
template<class U>
explicit Frac(const Frac<U>& rhs) :
numerator(rhs.numerator), denominator(rhs.denominator) {}
template <typename U>
Frac& operator+=(const Frac<U>& rhs) {
denominator += rhs.denominator; // ok: rhs has befriended Frac<T>
return *this;
}
template <typename U>
bool operator==(const Frac<U>& rhs) const {
// ok: rhs has befriended Frac<T> here too
return numerator == rhs.numerator && denominator == rhs.denominator;
}
private:
T numerator{}, denominator{};
};
// This free function doesn't need to be a friend. It uses the member function
// operator+=
// The returned type, Fact<R>, is deduced by fetching the type you'd gotten
// if you add a T and U.
template<typename T, typename U,
typename R = decltype(std::declval<T>() + std::declval<U>())>
Frac<R> operator+(const Frac<T>& lhs, const Frac<U>& rhs) {
Frac<R> rv(lhs); // use the converting constructor
rv += rhs;
return rv;
}
int main() {
Frac<int> foo;
Frac<double> bar;
auto r = foo + bar; // r is a Frac<double> (int + double => double)
}