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

时间:2014-12-08 15:44:38

标签: c++ templates

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

我想要的是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的能力。

2 个答案:

答案 0 :(得分:1)

我不是百分百肯定,因为我还没有尝试过所有新的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;
}

答案 1 :(得分:0)

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;
}

试试这个