具有不变返回类型的模板类中的交换运算符
commutative operators in a template class with an invariant return type
我有一个相当简单的模板类,如下所示。它本质上是 C++11 中 std::shared_ptr 的扩展,我想在其中重载各种运算符。对于这个问题,乘法运算符*((,我希望它是可交换的。
template<typename T>
class PoolVar {
private:
T * Variable;
public:
T GetValue() const {
assert(Variable != 0x0);
return (*Variable);
}
template < typename U >
friend T operator*(const PoolVar<T> & lhs, const PoolVar<U> & rhs) {
return (lhs.GetValue() * rhs.GetValue());
}
}
此实现的问题是,当我将此 PoolVar 类与定义如下的向量和四元数类一起使用时。两者都定义了乘法运算符,以便它对类的任何用户都是可交换的。
class Vector {
public:
friend class Quaternion;
protected:
double V_Value[3];
public:
Vector operator*(const Quaternion & q) const;
}
class Quaternion {
public:
friend class Vector;
protected:
double Q_Scalar;
Vector Q_Vector;
public:
Vector operator*(const Vector & v) const;
}
我想要的是 PoolVar 重载运算符本质上只是任何数据类型的传递,这样我就可以执行以下操作。假设我正确分配并初始化了 PoolVar 对象中的变量。
PoolVar<Vector> v;
PoolVar<Quaternion> q;
/* Stuff to create and initialize correctly. */
Vector v1 = v*q; // works currently
Vector v2 = q*v; // does not
Quaternion q1 = q*q; // ambiguous
当我尝试将另一个友元运算符(如下所示(添加到 PoolVar 以实现上面不起作用的模式时,我在编译时收到一堆错误消息,用于上面标记的不明确运算符。
template < typename U >
friend T operator*(const PoolVar<U> & lhs, const PoolVar<T> & rhs) {
return (lhs.GetValue() * rhs.GetValue());
}
我发现唯一有效的方法是显式定义 PoolVar 类之外运算符的各种组合,例如:
inline Vector operator*(PoolVar<Vector> & lhs, PoolVar<Quaternion> & rhs) {
return (lhs.GetValue() * rhs.GetValue());
}
inline Vector operator*(PoolVar<Quaternion> & lhs, PoolVar<Vector> & rhs) {
return (lhs.GetValue() * rhs.GetValue());
}
如果我能帮助它的话,我真的不想这样做,因为它是对 PoolVar 类的严重限制,并且会增加任何新的特殊类(如 Vector 和 Quaternion(的代码库维护。
问题不在于我可以创建一个交换运算符(我认为(,而在于两个定义的返回类型是相同的(不变的(,除非明确定义,否则会导致不明确的重载。
有没有办法设置一个模板类来具有不变返回类型的交换运算符,例如这个乘法运算符?另外,我应该指出,我没有利用C++11的奢侈,如果这种可能性会有所帮助的话。
我不是 100% 确定,因为我还没有尝试过所有新的 C++11 功能,但似乎这可以通过尾随返回类型来解决,从而允许编译器推断返回类型。我从您的片段中整理了一个例子:
template<typename T>
class PoolVar {
private:
T * Variable;
public:
T GetValue() const {
return (*Variable);
}
template <typename U>
friend auto operator*(const PoolVar<T> & lhs, const PoolVar<U> & rhs) -> decltype(lhs * rhs) {
return (lhs.GetValue() * rhs.GetValue());
}
};
class Quaternion;
class Vector {
public:
friend class Quaternion;
protected:
double V_Value[3];
public:
Vector operator*(const Quaternion & q) const;
};
class Quaternion {
public:
friend class Vector;
protected:
double Q_Scalar;
Vector Q_Vector;
public:
Vector operator*(const Vector & v) const;
};
int main(int argc, char** argv)
{
PoolVar<Vector> v;
PoolVar<Quaternion> q;
/* Stuff to create and initialize correctly. */
Vector v1 = v*q;
Vector v2 = q*v;
return 0;
}
由于我在Windows上,Visual Studio 2012似乎不支持此功能,因此我尝试在Ubuntu VM和 http://gcc.godbolt.org/上使用gcc编译它,但在这两种情况下编译器都崩溃了。谁能提示这是否是此问题上下文中尾随返回类型的适当/不当使用?
编辑
我明白最初的例子有什么不好,它引入了无限深度递归,因为乘法运算符的计算将产生相同模板的评估。但是当我尝试时:
template <typename U>
friend auto operator*(const PoolVar<T> & lhs, const PoolVar<U> & rhs) -> decltype(lhs.GetValue() * rhs.GetValue()) {
return (lhs.GetValue() * rhs.GetValue());
}
这给出了有关访问不完整类型 PoolVar
的错误,因此我将运算符移到了类范围之外:
template<typename T>
class PoolVar {
private:
T * Variable;
public:
T GetValue() const {
return (*Variable);
}
};
template <typename T, typename U>
auto operator*(const PoolVar<T> & lhs, const PoolVar<U> & rhs) -> decltype(lhs.GetValue() * rhs.GetValue()) {
return (lhs.GetValue() * rhs.GetValue());
}
class Quaternion;
class Vector {
public:
friend class Quaternion;
protected:
double V_Value[3];
public:
Vector operator*(const Quaternion & q) const;
};
class Quaternion {
public:
friend class Vector;
protected:
double Q_Scalar;
Vector Q_Vector;
public:
Vector operator*(const Vector & v) const;
};
int main(int argc, char** argv)
{
PoolVar<Vector> v;
PoolVar<Quaternion> q;
/* Stuff to create and initialize correctly. */
Vector v1 = v*q;
Vector v2 = q*v;
return 0;
}
这似乎编译得很好,所以我想我最初的想法是正确的,谢谢你的好问题,到目前为止,我还没有一个好的例子来尝试尾随返回类型。
编辑 2
GCC 有一个名为 typeof
的内置扩展,可以在没有 C++11 的情况下解决这个问题,但话又说回来,它是 GCC 特定的:
template<typename T, typename U>
class EvaluateResult
{
static T m_a;
static U m_b;
public:
typedef typeof(m_a * m_b) Result;
};
template<typename T>
class PoolVar {
private:
T * Variable;
public:
T GetValue() const {
return (*Variable);
}
};
template <typename T, typename U>
typename EvaluateResult<T,U>::Result operator*(const PoolVar<T> & lhs, const PoolVar<U> & rhs) {
return (lhs.GetValue() * rhs.GetValue());
}
class Quaternion;
class Vector {
public:
friend class Quaternion;
protected:
double V_Value[3];
public:
Vector operator*(const Quaternion & q) const;
};
class Quaternion {
public:
friend class Vector;
protected:
double Q_Scalar;
Vector Q_Vector;
public:
Vector operator*(const Vector & v) const;
};
int main(int argc, char** argv)
{
PoolVar<Vector> v;
PoolVar<Quaternion> q;
/* Stuff to create and initialize correctly. */
Vector v1 = v*q;
Vector v2 = q*v;
}
template<typename T>
class PoolVar {
private:
T * Variable;
public:
T GetValue() const {
return (*Variable);
}
template < typename U >
friend typename T::type operator*(const PoolVar<T> & lhs, const PoolVar<U> & rhs) {
return (lhs.GetValue() * rhs.GetValue());
}
// to handle equl types :)
friend T operator*(const PoolVar<T> & lhs, const PoolVar<T> & rhs) {
return (lhs.GetValue() * rhs.GetValue());
}
};
class Vector {
public:
friend class Quaternion;
protected:
double V_Value[3];
public:
Vector operator*(const Quaternion & q) const{ return Vector(); }
Vector operator*(const Vector & q) const{ return Vector(); }
typedef Vector type;
};
class Quaternion {
public:
friend class Vector;
protected:
double Q_Scalar;
Vector Q_Vector;
public:
Vector operator*(const Vector & v) const {return Vector();};
Quaternion operator*(const Quaternion & v) const {return Quaternion();};
typedef Vector type;
};
int main()
{
PoolVar<Vector> v;
PoolVar<Quaternion> q;
Vector v1 = v * q; // good
Vector v2 = q * v; // good
Quaternion qq = q * q; // good
Vector vv = v * v; // good
return 0;
}
试试这个
- 交换运算符 + 重载会导致无限递归
- C++ - 没有自定义交换功能的移动分配运算符?
- 为什么基类中的复制和交换会导致派生类中的复制赋值运算符被隐式删除?
- 为什么标准在移动分配运算符中使用交换?
- 运算符重载的隐式交换性
- 如何为模板嵌套类编写可交换的重载算术运算符?
- &&&运算符的交换性质问题
- 如何在没有复制赋值运算符的情况下交换两个对象
- 如何重载加法运算符以进行交换计算
- 运算符+=可以调用自己,但"*this"与"rhs"交换吗?
- 是具有复制和交换习惯用法的复制赋值运算符,建议进行自赋值检查
- 交换与使用std::map[]运算符赋值(const问题)
- 异或交换算法中运算符的未定义行为
- 原子比较运算符(无交换)
- 为什么内存泄漏只发生在赋值运算符重载的情况下,而不是在复制构造函数中,以及复制和交换习惯用法如何解决它
- 通过 xor 运算符使用引用或指针交换两个变量的值
- 在什么情况下,运算符=应该用左值/右值重载而不是复制和交换来实现
- 具有不变返回类型的模板类中的交换运算符
- 如何在赋值运算符中使用 noexcept 与复制和交换成语
- C/ c++编译器会对交换运算符(例如:+,*)重新排序以优化常量吗?