【问题标题】:Overriding functions (from two abstract base classes) that differ only by their return value覆盖仅返回值不同的函数(来自两个抽象基类)
【发布时间】:2013-08-30 09:19:21
【问题描述】:

我想创建一个实现两个接口的类,这两个接口具有相同名称的函数,只是它们的返回值不同。我怎样才能正确地做到这一点?

template<class T>
class IBase
{
public:
    virtual T Method() = 0;
};

class Derived : public IBase<int>, public IBase<char> {
public:
    int _value;

    virtual int IBase<int>::Method();
    virtual char IBase<char>::Method();
};

int Derived::Method() {
    return _value;
}

char Derived::Method() {
    return _value;
}

这是我得到的错误:

error C2555: 'Derived::Method: overriding virtual function return type differs and is not covariant from 'IBase<int>::Method

error C2556: 'int Derived::Method(void)' : overloaded function differs only by return type from 'char Derived::Method(void)'

error C2371: 'Derived::Method: redefinition; different basic types

error C2084: function 'char Derived::Method(void)' already has a body

在 C# 中,使用几乎相同的语法(称为显式接口实现)很容易做到这一点,没有任何歧义:

class Derived : IBase<int>, IBase<char> {
    int _value;

    int IBase<int>.Method() {
        return _value;
    }

    char IBase<char>.Method();
        return _value;
    }
};

显式实现是私有的,因此不能直接用于Derived 类的变量。它们仍然非常有用,因为您可以将 Derived 强制转换为接口之一以使用实现:

var d = new Derived();
((IBase<int>)d).Method();

这可能非常有用。一个类可以多次实现 ICanConvertTo 以启用不同的转换。

【问题讨论】:

  • 顺便说一句,GCC 给出了不同的错误:ideone.com/3f0ZVG (cannot declare member function ‘IBase&lt;int&gt;::Method’ within ‘Derived’char 相同,而不是 int,然后 no ‘int Derived::Method()’ member function declared in class ‘Derived’char 相同,而不是 @ 987654334@)
  • @gx_ 你删除了你的评论吗?我觉得挺好用的。
  • 是的,我做到了,因为我意识到这实际上不是同一个问题(您的返回类型不同)。无论如何,既然你问了=)这个问题有点相关:stackoverflow.com/questions/18398409/…(在模板实例化之后,IBase&lt;int&gt;IBase&lt;char&gt; 是两个不同的不相关的类)
  • 我认为你是对的,返回类型的差异并不重要。重要的是名称和签名相同(不好),但完全限定的名称和插槽不同(本可以救我)。

标签: c++ templates inheritance multiple-inheritance


【解决方案1】:

Derived 中带有 virtual

#include <iostream>

template<typename T>
class IBase
{
    public:
    virtual T method() = 0;
};

template<typename T>
class WrapBase : public IBase<T>
{
    protected:
    virtual T do_method(T*) = 0;

    public:
    virtual T method() {
        return do_method((T*)0);
    }
};

class Derived : public WrapBase<char>, public WrapBase<int>
{
    protected:
    virtual char do_method(char*) { return 'A'; };
    virtual int do_method(int*) { return 1; };
};

在 Derived 中删除虚拟 - 感谢 DyP

include <iostream>

template<typename T>
class IBase
{
    public:
    virtual T method() = 0;
};

template<typename D, typename T>
class WrapBase : public IBase<T>
{
    public:
    virtual T method();
};

class Derived : public WrapBase<Derived, char>, public WrapBase<Derived, int>
{
    friend class WrapBase<Derived, char>;
    friend class WrapBase<Derived, int>;

    protected:
    char do_method(char*) { return 'A'; };
    int do_method(int*) { return 1; };
};

template<typename D, typename T>
inline T WrapBase<D, T>::method() {
    return static_cast<D*>(this)->do_method((T*)0);
}

测试:

int main () {
    Derived d;
    IBase<char>& c = d;
    IBase<int>& i = d;
    std::cout << c.method() << " != " << i.method() << std::endl;
}

评论:混合静态和动态多态性可能是一个糟糕的设计。

【讨论】:

  • 有趣!你在这里考虑静态多态是什么?
  • @Ark-kun 模板
  • 我的目标是使解决方案完全静态且“可内联”。我认为您的解决方案可以做到这一点。
  • 你可以使用WrapBase中的CRTP(至少)来摆脱第二个虚函数调用。有了 C++11 支持,我认为 final 说明符也可以帮助 WrapBase::methoddo_method 可以是private
  • 注意:我的建议是无法覆盖do_method 函数是派生自Derived 的类(你也不能覆盖原始method)。
【解决方案2】:

函数不能仅通过返回值来区分,因为编译器无法区分它们。考虑:

long x;
Derived d;
x = d.Method();

charint 变体都可以转换为long - 它应该使用哪一个?

编辑: 如果你想定义转换,典型的情况是定义一个 cast-operator,例如

class X 
{
     float x;
   public:
     X(float f) : x(f) {}
     operator int() { return static_cast<int>(x); }
     operator char() { return static_cast<char>(x); }
     float getX() { return x; }
};

然后将其称为:

X x(65.3);
int y = x;
char z = x;

cout << "x.f=" << x.getF() << " as char:" << z << " as int:" << y << endl;

【讨论】:

  • 我对此表示怀疑。我听说有一种方法可以显式调用其中一个函数。类似d.IBase&lt;int&gt;::Method();
  • 调用d.Method(); 是模棱两可的,被禁用是理所当然的。但这不应该阻止我实施这些方法。我可以像((IBase&lt;int&gt;)d).Method() 一样明确地称呼它。就像我在 C# 中可以做的那样。
  • @Ark-kun 你问的是c++还是c#?
  • 你已经注意到编译器并没有抱怨你在哪里使用该方法,而是你在哪里声明它,对吧?
  • @BЈовић C++。我可以在 C# 中轻松完成此操作(请参阅更新的问题),但我需要在 C++ 中执行此操作。
【解决方案3】:

返回值类型不是函数(方法)签名的一部分。

所以你的两种方法被视为相同的方法(所以重新定义错误)。

所以你不能做你想做的事。你的方法应该有不同的签名。

【讨论】:

  • 函数有很大的不同——它们来自不同的基类。可以说它们有不同的名称:IBase&lt;int&gt;::Method()IBase&lt;char&gt;::Method()
  • 目前,函数在Derivated中。如果你给 IBase::Method() 提供实现并删除 Derivated::Method() 的声明,你可以调用 derivated.IBase::Method(),但你不能重新定义 Derivated::Method ()
  • @Jarod42 是派生的,而不是“派生的”:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-25
  • 2022-08-18
  • 1970-01-01
  • 2023-04-04
相关资源
最近更新 更多