具有不变返回类型的模板类中的交换运算符

commutative operators in a template class with an invariant return type

本文关键字:交换 运算符 返回类型      更新时间:2023-10-16

我有一个相当简单的模板类,如下所示。它本质上是 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;
}

试试这个