抽象类的逆变性

6

我希望在C++上创建一个漂亮的界面,每个实现都需要在自身上定义其附加部分。

我想做类似于这样的事情:

    class A{
        ...
        virtual A& operator+(const A& other) =0;
        ...
    }
    // this is my interface or abstract class.


    class B : A{
        ...
        B& operator+(const B& other);
        ...
    }
    // this is the kind of implementation i would like. a B element can be added by another B element only ! At least this the constraint I am aiming at.

由于C++不支持逆变,因此我的函数B& operator+(const B& other)没有实现virtual A& operator+(const A& other)。有没有什么巧妙(但略微简洁)的方法可以做到这一点?


6
据我所知,这不是逆变(contravariance)。逆变应该是以一个比基类声明中更一般的类型作为参数。 - Quentin
1
你能给我们提供这个的使用案例吗?通常尝试添加语言功能并不是解决问题的最佳方式。 - IdeaHat
1
如果它不接受任意(A的子类)作为参数,那怎么可能是重载呢?(如果我没记错,逆变性是在另一个“方向”上进行的 - 在某些语言中,operator+(const A&other)可能是operator+(const B&other)的有效重写,因为它更“通用”,但反过来则不行)。 - molbdnilo
你是想强制实现一些函数,还是希望 class B : Aclass C : A 都可以作为 A 使用? - foobar
你能这样做吗?:template class A { virtual T& operator+(const T& other) =0; };使用类 class B : A<B>{...}; - tgmath
显示剩余12条评论
2个回答

7

2
CRTP 真是太棒了。需要注意的是 A<B> 和 A<C> 没有关系,但你可以使用更多的基类(请参见我的上面的注释)。 - IdeaHat
如果您不介意的话,我已经添加了一个CRTP参考。实际上,您可能最终会得到一个class C,但我在这里只提供了基本框架,以帮助OP入门。 - Bathsheba
1
那么关键是什么呢?由于A<B>A<C>是独立的,因此将该方法设置为虚拟方法没有任何意义,因为你无法以多态方式调用它。 - Jan Hudec
@JanHudec 嗯,这意味着 class C : public A<B> 可以是运行时多态的,可能会有用吧?虽然我会让它非虚拟并使用编译时多态。 - IdeaHat
@Bathsheba: 谢谢你的建议,我对这种技术不是特别感兴趣,但这是目前为止我见过最方便的解决方案,作为最后一招,我会采用这种技术。 - Mathieu Bouyrie

5

你想要做的事在任何语言中都是不可能的,因为它没有意义。

在某些语言中,方法的参数类型是逆变的,但这意味着如果方法接受超类型,那么该方法就是一个重载。例如,operator+(const A&) 将成为 operator+(const B&) 的重载。但反过来就不行了。因为当你有两个A实例(我们称它们为xy)并写下x + y时,方法将被调用,编译器无法知道两者是否属于相同的子类型,因为这些信息只有在运行时才能得到。所以在运行时是唯一可以检查的时间。

所以:

  1. 如果您确实需要接口,并且将使用它进行多态操作,则运算符必须获取该接口并在运行时检查其是否获得了兼容的实例(但这不会激发对设计的信任)。
  2. 如果您不需要进行多态操作,则根本不需要定义接口。只需在将使用它的模板中使用+(如果它不是多态的,则必须是模板),编译器在未定义时会出现错误。您可以编写concept check class以提前查找并避免过深的模板扩展堆栈而出错。

非常感谢!实际上我的主要目的是在向量空间上定义数学函数(因此我需要通用地定义什么是向量空间)。最终目的是对这些空间进行数学优化。我不熟悉"concept check class"这个概念,我将看看它是什么。编辑:那似乎正是我需要的,谢谢!!!! - Mathieu Bouyrie
@MathieuBouyrie:还要看一下Boost.Operators实用头文件。它提供了各种派生运算符的帮助程序,只要提供基本运算符即可。它主要用于定义完整的比较套件,但也可以从增强赋值(即从+=中获得+;后者通常更有效率)等方面定义二元运算符。它向你展示了可以使用的技术。 - Jan Hudec

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