[C++]协变返回类型

4

我有一个 VectorN 类和一个从 VectorN 继承的 Vector3 类(例如可以处理叉积)。 我在确定不同运算符的返回类型时遇到了困难。 例如:

class VectorN
{
public:
   VectorN(){};
   virtual VectorN operator*(const double& d) {.....};
   std::vector<double> coords;
};

class Vector3 : public VectorN
{
public:
  Vector3(){};
  virtual Vector3 operator*(const double& d) {....};
};

这个例子会产生C2555错误:

'Vector3::operator *':覆盖虚函数的返回类型与 'VectorN::operator *' 不同且不是协变的,参见 'VectorN::operator *' 的声明。

问题在于我没有返回对 Vector3 的引用,而且在声明 operator*Vector3 类还没有完全定义。但是,我希望我的 operator* 是虚的,并且在将一个 Vector3 乘以一个常数时返回一个 Vector3 (否则,如果我执行 (Vector3* double).crossProduct(Vector3),将会出现错误)。

我该怎么办?

谢谢!


替换对象为指针的方法是使用auto_ptr/unique_ptr,但这些指针不能协变!X-P。 - Potatoswatter
顺便说一下,不要通过const&传递原始类型,应该通过值传递。(不要通过const&或任何大小小于sizeof(void*)的东西传递任何原始类型。) - GManNickG
既然我已经回答了这个问题……为什么你不使用现成的数值库呢?这个功能甚至在标准库中都有valarray - Potatoswatter
因为这个类的目标不仅仅是进行叉积和乘法运算。在Vector3类中,还有颜色转换函数或随机采样具有不同概率密度函数的向量等等。这不是一种通用的使用方式。 - nbonneel
1
@user:那些东西应该是自由函数。始终优先选择自由函数。 - GManNickG
3
这样的功能是建立在像valarray(或数十个免费替代品之一)这样的库之上。重新实现向量和点积会让你走上错误的道路,很难回头。而生成随机向量或转换颜色空间应该是自由函数(在类外部),而不是方法。一个类不是一个库。 - Potatoswatter
2个回答

6

您需要重新设计。首先,优先使用自由函数而不是成员函数。您应该只有需要访问私有成员的成员函数。

从以下组合开始:

class VectorN
{
public:
   virtual VectorN& operator*=(double d)
    {
        /* ... */

        return *this;
    };
};


class Vector3 : public VectorN
{
public:
    virtual Vector3& operator*=(double d)
    {
        return static_cast<Vector3&>(VectorN::operator*=(d));
    };
};

在这里,协方差因为类型是引用或指针而正常工作,您可以重复使用代码。(由于您了解派生类型,因此static_cast是免费且安全的。)

然后您可以实现自己的自由函数:

// optimization: if you're going to make a copy, do it in the parameter list;
// compilers can elide a copy when working with temporaries
VectorN operator*(VectorN v, double d)
{
    // reuse code
    return v *= d;
}

VectorN operator*(double d, VectorN v)
{
    // reuse code
    return v *= d;
}

使用相同的方法处理Vector3

这样做的好处是你可以轻松编写这些运算符,因为你可以使用运算符的核心部分,并且由于协变性,返回类型匹配。


但是请注意警告,你可能不需要其中的任何内容。如果你想要扩展,可以通过操作vectorvalarray的自由函数来实现。


或者...只需使用valarray。同时拥有vectorNvector3表明他想要一个可以根据大小进行模板化的代数向量库。你知道有没有一个简单的吗? - Potatoswatter
+1:非常好!不过,当你说“将v放在const VectorN之上,以便让Vector3使用它…”时,你是不是漏掉了一个&符号?假设参数需要是一个引用,以避免切片和在*=上丢失虚拟调度? - Troubadour
1
@Troubador:不,他建议通过值传递和返回,并复制所有函数。这对于模板确实是一个问题。 - Potatoswatter
@Troubadour:是的,但仔细看一下就毫无意义了。 - GManNickG
嗯...但对于不是运算符的函数,我不太喜欢写getNormalized(myVector3),而是更喜欢写myVector3.getNormalized(),这似乎更自然 :s - nbonneel
显示剩余2条评论

0
我能想到的最好方法是用智能指针替换返回类型,放弃协变性而选择多态性:
virtual auto_ptr< VectorN > operator*(const double& d);

我建议这样做的原因是你正在使用虚函数,因此不需要知道对象的确切类型。
根本问题在于调用者需要为按值返回的对象分配存储空间。该存储空间无法动态变化,因此你不可避免地要在堆上分配对象。

我不熟悉auto_ptr,但我会去看一下...谢谢! :) - nbonneel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接