代码之家  ›  专栏  ›  技术社区  ›  zezba9000

只返回不同类型的C++多个接口?

  •  5
  • zezba9000  · 技术社区  · 7 年前

    用人类可读的方式做一个将.NET IL翻译成C++的实验。

    问题是: C允许您解析具有相同方法名的多个接口,但返回类型不同。C++似乎并没有支持这一点,但是用VTILE解决两个接口是不可能的(或者我错了吗?).

    我已经找到了一种用模板复制C++中的c++方法的方法。 我想知道是否有一种方法不需要解决相同问题的模板? 模板是冗长的,如果可能的话,我不希望在每个接口类型中使用它们。

    这里是C++版本。

    template<typename T>
    class IMyInterface
    {
        public: short (T::*Foo_IMyInterface)() = 0;
    };
    
    template<typename T>
    class IMyInterface2
    {
        public: int (T::*Foo_IMyInterface2)() = 0;
    };
    
    class MyClass : public IMyInterface<MyClass>, public IMyInterface2<MyClass>
    {
        public: MyClass()
        {
            Foo_IMyInterface = &MyClass::Foo;
            Foo_IMyInterface2 = &MyClass::IMyInterface2_Foo;
        }
    
        public: virtual short Foo()
        {
            return 1;
        }
    
        private: int IMyInterface2_Foo()
        {
            return 1;
        }
    };
    
    class MyClass2 : public MyClass
    {
        public: virtual short Foo() override
        {
            return 2;
        }
    };
    
    void InvokeFoo(IMyInterface<MyClass>* k)
    {
        (((MyClass*)k)->*k->Foo_IMyInterface)();
    }
    
    void main()
    {
        auto a = new MyClass2();
        InvokeFoo(a);
    }
    

    这里是C++的一个参考源。

    interface IMyInterface
    {
        short Foo();
    }
    
    interface IMyInterface2
    {
        int Foo();
    }
    
    class MyClass : IMyInterface, IMyInterface2
    {
        public virtual short Foo()
        {
            return 1;
        }
    
        int IMyInterface2.Foo()
        {
            return 1;
        }
    }
    
    class MyClass2 : MyClass
    {
        public override short Foo()
        {
            return 2;
        }
    }
    
    namespace CSTest
    {
        class Program
        {
            static void InvokeFoo(IMyInterface k)
            {
                k.Foo();
            }
    
            static void Main(string[] args)
            {
                var a = new MyClass2();
                InvokeFoo(a);
            }
        }
    }
    

    这个C++方法 在下面不工作 但我希望它能做到(更多的是我要做的)。

    class IMyInterface
    {
        public: virtual short Foo() = 0;
    };
    
    class IMyInterface2
    {
        public: virtual int Foo() = 0;
    };
    
    class MyClass : public IMyInterface, public IMyInterface2
    {
        public: virtual short Foo()
        {
            return 1;
        }
    
        private: int IMyInterface2::Foo()// compiler error
        {
            return 1;
        }
    };
    
    class MyClass2 : public MyClass
    {
        public: virtual short Foo() override
        {
            return 2;
        }
    };
    
    void InvokeFoo(IMyInterface* k)
    {
        k->Foo();
    }
    
    void main()
    {
        auto a = new MyClass2();
        InvokeFoo(a);
    }
    
    4 回复  |  直到 7 年前
        1
  •  8
  •   fstam    7 年前

    问题是,不能仅基于返回类型进行重载。

    见

    最后一个stackoverflow线程指出,可以使用运算符进行重载。

    struct func {
        operator string() { return "1"; }
        operator int() { return 2; }
    };
    
    int main() {
        int x = func(); // calls int version
        string y = func(); // calls string version
        double d = func(); // calls int version
        cout << func() << endl; // calls int version
        func(); // calls neither
    }
    

    但是你不能说出他们的名字,这会很快变成一个混乱的工作。

    参数列表必须更改。Victor Padureau建议使用void返回类型,并将值的类型作为引用传递给要在方法中设置的值,这将起作用。还可以使用“更改不同类型的方法名”。

    class my_interface
    {
    public: 
        virtual short foo_short() = 0;
    };
    
    class my_interface2
    {
    public: 
        virtual int foo_int() = 0;
    };
    
    class my_class : public my_interface, public my_interface2
    {
    public: 
        short foo_short() override
        {
            return 1;
        }
    
        int foo_int() override
        {
            return 1;
        }
    };
    
    class my_class2 : public my_class
    {
    public: 
        virtual short foo_short() override
        {
            return 2;
        }
    };
    
    void InvokeFoo(my_interface* k)
    {
        short result = k->foo_short();
        std::cout << result << std::endl;
    }
    
    void main()
    {
        auto a = new my_class2();
        InvokeFoo(a);
    }
    
        2
  •  5
  •   Victor Padureanu    7 年前

    我有一个可行的解决方案。这并不完美,但如果你移植的话,这是一种解决问题的方法。

    而不是打电话 int foo() 你可以打电话 void foo(int& out) 这样,您就可以将返回类型放入函数的调用部分。

        3
  •  1
  •   Aconcagua    7 年前

    最好的是: not having the same function names in both interfaces , making return values output parameters 当然是一个有趣的选择。为了完整性:假设我们不能更改基类的接口,可能是因为来自不同的库——我们仍然需要重写。

    首先,继承这两个接口可能是一个有问题的设计,很可能会违反 single responsibility principle .

    现在假设我们无论如何都需要。一个可能的解决方法(它不符合我自己对“优雅”的定义——但至少……)是中级课程:

    class BaseA { virtual ~BaseA(); virtual void foo(); };
    class BaseB { virtual ~BaseB(); virtual int foo(); };
    
    class DerivedA : public BaseA { void foo() override; };
    class DerivedB : public BaseB { int foo() override; };
    
    class Derived : public DerivedA, public DerivedB { };
    

    现在 Derived 两者都可以 BaseA 和 BaseB 并且仍然覆盖了foo的两个变体(在本例中),尽管是间接的。

    如果你打算进一步继承 衍生的 ,仍允许重写 foo 函数,然后在 DerivedA 和 DerivedB 本身会调用(新)虚拟函数 fooA 和 fooB 分别:

    class DerivedA : public BaseA
    {
    public:
        void foo() final // better than override:
                         // prevents breaking the pattern again
        { fooA() };
    protected:
        virtual void fooA() = 0;
    };
    class DerivedB : public BaseB
    {
    public:
        int foo() final { return fooB() };
    protected:
        virtual int fooB() = 0;
    };
    

    同样的技巧允许重写使用两个基的成员,当然 最干净的 然而,要完成这一任务,需要做一些权衡,因为虚拟函数调用不是免费的(vtable lookup,然后是实际函数调用),所以如果性能更重要(如果它 真的? 是!),您可能只是在 派生词 和 派生B 直接。

        4
  •  0
  •   Phil1970    7 年前

    如果您希望C++代码基本上类似于您的C语言示例,我建议您基本上添加一个额外的中间类。 MyClass_IMyInterface2 唯一的目的是重命名在C代码中显式实现的方法。

    因此,您可能会遇到如下情况:

    class IMyInterface2
    {
    public: 
        virtual int Foo() = 0;
    };
    
    class MyClass_IMyInterface2 : public IMyInterface2
    {
    protected:
        int Foo() final { return IMyInterface2_Foo(); }
        virtual int IMyInterface2_Foo() = 0; 
    };
    
    class MyClass : public IMyInterface, public MyClass_IMyInterface2...
    

    顺便说一下,如果有冲突,只需要添加额外的类,如果有冲突,只需要进行重命名。

    假设目的是模仿C代码,我可能会在每次显式实现接口方法时都这样做。

    还可以根据需要调整成员的可见性。