做一个以人类可读的方式将.NET IL转换为C ++的实验。
这是问题所在:C#允许您使用相同的方法名称解析多个接口,只是返回类型不同。 C ++似乎不支持此功能,但是使用vTable无法解析两个接口(或者我错了吗?)。
我找到了一种使用模板在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 ++所基于的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);
}
答案 0 :(得分:8)
问题在于您不能仅基于返回类型进行重载。
请参见
最后一个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);
}
答案 1 :(得分:5)
我有一个可行的解决方案。这并不完美,但是如果您要移植,这是解决此问题的一种方法。
您可以通过将返回类型放入函数的调用部分中的方式来调用int foo()
,而不是调用void foo(int& out)
。
答案 2 :(得分:1)
最好的是: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 { };
现在 如果您打算进一步继承 相同的技巧允许重写使用两个基数的成员,并且肯定是解决问题的最干净的方法,但是,由于虚拟函数调用并不适用,因此存在一些折衷免费(vtable查找,然后进行实际函数调用)–因此,如果性能更重要(如果真的是,请考虑一下!),您可以只在Derived
可以同时用作BaseA
和 BaseB
,并且仍然覆盖了foo的两个变体(在这种情况下),尽管是间接的。>
Derived
,但仍允许覆盖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;
};
DerivedA
和{{1 }}。
答案 3 :(得分:0)
如果您想要本质上与C#示例相似的C ++代码,我建议您本质上添加一个额外的中间类MyClass_IMyInterface2
,其唯一目的是重命名在C#代码中明确实现的方法。 / p>
因此,您将得到类似的内容:
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#代码,那么每次明确实现接口方法时,我可能都会这样做。
您还可以根据需要调整成员的可见性。