【问题标题】:How to implement a polymorphic parameter to several interfaces?如何实现多个接口的多态参数?
【发布时间】:2016-09-09 05:57:47
【问题描述】:

给定一些基本接口 A、B、C.... N

还有一些实现它们的假设类:

class MyClass1: public A, public B, public N;
class MyClass2: public A, public B, public D;

我想创建一个函数,它接受一个对象(或指向对象的指针)作为参数,该类包括 A 和 B。

void myFunc( A&B obj );

从 C++ 程序员的角度来看,这似乎很荒谬。从软件架构师的角度来看,这似乎是所有语言都应该具备的基本块。

有什么技巧(除了模板)或解决方案吗?

注意:大部分接口不是来自我,所以(原则上)不可能对接口进行更改。

编辑: 这里有一些例子:

class A{
public:
  virtual void draw()=0;
};

class B{
public:
  virtual void edit()=0;
};


class AandB: public A, public B
{
  virtual void draw()override{};
  virtual void edit()override{};
};

//This function know about A, B, but not AandB
void some_function((A+B)& a_and_b) { // That do not work
  a_and_b.draw();
  a_and_b.edit();
}

int main()
{
  AandB c;
  some_function(c); // that is not so easy
}

【问题讨论】:

  • 在我看来 MyClass 继承 AB 的事实与问题无关。 AB 是不相关的类型,所以真正的问题是如何编写一个可以接受两种不相关类型之一的函数。
  • @Galik:这就是接口的重点:定义一些行为。 A 是一种行为(可绘制?),B 是另一种行为(可编辑?)。因此该函数接受任何可编辑和可绘制的类。所以你的问题的答案是:不是 A 或 B,而是 A 和 B。
  • 嗯,好的。所以你想接受任何同时实现AB 的类。模板其实就是解决这个问题的方法,你为什么不想要一个模板解决方案呢?
  • 我不喜欢模板,因为它是非常强大但不安全的功能之一 1)强制定义在标题中,难以维护 2)难以使用多态指针管理,3)它与其他语言的更好特性,如多态性、重载函数等。 4)它使一些其他特性(如虚拟)变得困难或不可能? 5) 代码难以检查,因为问题仅在使用阶段很明显。笼统地说:模板是用于一般编程的,IMO 不应该被用作代码重用、继承限制和“一切”的小丑。
  • 嗯,好的。这很有趣,因为在继承和模板方面,我的感觉大多与你相反。 ;o)

标签: c++ c++11 interface polymorphism code-reuse


【解决方案1】:
#include <type_traits>

using namespace std;

class A{};
class B{};

template<class T, enable_if_t<is_same<A, decay_t<T>>::value || is_same<B, decay_t<T>>::value, int> = 0>
void some_function(T&& a_or_b) {}

直播:https://godbolt.org/g/Z1MV8w

这个例子需要c++14,但是如果你需要11的兼容性,你可以使用enable_if来代替。它只是不那么可读。

如果您想获取从 A 或 B 继承的任何内容,请使用 is_base_of 而不是 is_same

【讨论】:

  • 我目前正在尝试根据我的情况调整此解决方案,我会尽快接受或进一步评论。
  • 你至少需要衰减Tis_base_of 也是一个有点可疑的检查。
  • 更新为 decay_t。为什么说它有问题?对于更细微的 TMP 细节,我还是很陌生。
  • 似乎有效,但模板正在通过所有类层次结构传播:-/
  • @AdrianMaire 这是什么意思? “通过所有的类层次结构”?
【解决方案2】:

您真正需要的是编译器创建一个中间类型 (A & B),其接口包含 interface Ainterface B。然后允许实现 both 那些接口的任何类型绑定到此组合类型的引用。

我什至不确定您如何在语法中引用这种格式塔类型:

void func((A+B)& var); // ???

您可以使用当前语法执行类似的操作,而无需编译器在后台创建脚手架,只需像这样接受参数 twice

struct A { void stuff(){ std::cout << "A\n"; } };
struct B { void stuff(){ std::cout << "B\n"; } };
struct C { void stuff(){ std::cout << "C\n"; } };

struct MyType: A, B, C
{
    void stuff() { std::cout << "MyType\n"; }
};

void func(A& a, B& b) // accept both interfaces in func
{
    a.stuff(); // use interface A
    b.stuff(); // use interface B
}

int main()
{
    MyType m;

    func(m, m); // pass both interfaces to func()
}

【讨论】:

  • 相当聪明。复制参数有点混乱,但仍然是个好主意。
  • 非常聪明 :) 接受单个参数并传递给 func 的模板函数呢?例如类似template&lt;class T&gt; func2(T &amp;a){ return func(a, a); }
【解决方案3】:

我对 xaxxon 的回答投了票,
但是如果您想以“虚拟”方式进行操作,
你可以做扩展AB的课程。

看起来像这样:

class A;
class B;
class A_B: public A, public B{
};

class MyClass1: public A_B, public XXX{
};

class MyClass2: public A_B, public YYY{
};

void myFunc( A_B obj );

我同意这看起来很像 Java。

【讨论】:

  • 在这种情况下 A_B 是否应该从 A 和 B 虚拟继承?如果 XXX 或 YYY 从 A 或 B 继承?
  • 感谢您尝试不使用模板。但实际上这是不可能的: - 需要更改 MyClass1 和 Myclass2 - 当使用超过 2 个接口时,它变得疯狂。
  • A_BA_XXXB_XXX等情况下会有问题。
  • 可能虚拟继承会有所帮助。但无论如何都会很乱。
【解决方案4】:

减轻模板缺点的一种方法

void myFunc_impl(A& aPart, B& bPart); // aPart and bPart are from the same object.

template <typename T>
void myFunc(T& obj) {
    // static_assert to have better error messages
    static_assert(std::is_base_of<A, T>::value, "T should inherit from A");
    static_assert(std::is_base_of<B, T>::value, "T should inherit from B");
    // Forwarding to implementation
    myFunc_impl(obj, obj);
}

1) 强制定义在头部,难以维护

您只需在标题中进行转发: 代码很短。

2) 多态指针难以管理,

只要你拿参考或指针,这部分如果模板没问题。

4) 让虚拟等其他功能变得困难或不可能?

确实,模板方法不能是模板,但是这里可以转发到虚拟方法。

5) 代码难以检查,因为问题仅在使用阶段就很明显。

您确实必须实例化它才能查看所有错误。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-10-08
    • 2015-04-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多