【问题标题】:template which enforces interface强制接口的模板
【发布时间】:2009-10-11 13:04:43
【问题描述】:

是否可以创建一个模板接受实现特定接口的类型? 例如,我想对模板用户说:你可以在我的容器中存储任何东西,只要它实现了Init()Destroy() 方法。

谢谢

【问题讨论】:

  • 这是stackoverflow.com/questions/1550370/…的副本,但这个问题措辞更好,更笼统=)
  • 不知道怎么理解这个问题。容器是否只存储一种类型(支持方法)或不同类型的元素?
  • 容器存储任何类型的元素,前提是该类型继承并实现了所需的接口。因此,例如,容器可以在插入/删除元素时调用 Element.Init() 和 Element.Destroy()。
  • 简单的答案是“编译器已经强制执行”。如果您的模板尝试调用 Init() 和 Destroy(),那么如果您尝试存储未定义这两个方法的对象,则会出现编译错误。

标签: c++ inheritance templates


【解决方案1】:

Boost Concept Check library 提供了有限的 C++0x 概念功能子集(有意但不幸被删减)。您可以通过为所需的接口创建concept check class 来利用它。

【讨论】:

  • 什么 C++0x 功能?概念没有成功。
  • 皮特:是的。我应该说“有潜力,但最终被删除”的 C++0x 特性。
【解决方案2】:

首先,如果您需要 Init 和 Destroy 的存在,则意味着模板代码在某处使用它们。这意味着,编译器已经检查了它们的存在,因为如果类型没有这些方法,模板将不会编译。

但是,如果您想检查它们,那么一种方法可能是在某些编译时上下文中使用它们的地址,例如

template <class T>
class X
{
private:
    template <unsigned N>
    struct Number {};
    Number<sizeof(&T::Init) + sizeof(&T::Destroy)> must_define_init_and_destroy();
};

struct A
{
    bool Init();
    void Destroy();
};

struct B {};

int main()
{
    X<A>();
    X<B>();
}

Comeau 的输出是:

"ComeauTest.c", line 7: error: class "B" has no member "Init"
          Number<sizeof(&T::Init) + sizeof(&T::Destroy)> must_define_init_and_destroy();
                            ^
          detected during instantiation of class "X<T> [with T=B]" at line 21

"ComeauTest.c", line 7: error: class "B" has no member "Destroy"
          Number<sizeof(&T::Init) + sizeof(&T::Destroy)> must_define_init_and_destroy();
                                               ^
          detected during instantiation of class "X<T> [with T=B]" at line 21

但是,如果任何一个必需的方法被重载,这就会失败,自然这仍然不能测试这些方法是否具有合适的原型。

例如,您可能期望 bool Init(int, int)。您可以使用 static_cast 来检查确切的签名,但这可能会对类型施加不必要的限制。例如,如果某个类使用 bool Init(long, long) 代替)呢?

不管怎样,这种努力似乎只是为了使错误消息更明显。但是,我非常怀疑在没有任何概念检查的情况下您会收到的任何消息(例如“在此处使用时没有合适的方法 Init 来使用 T = X 调用”)是那么糟糕。

【讨论】:

    【解决方案3】:

    不,这是不可能的。

    模板被替换为 一个 类型,而不是许多不同的类型。

    考虑创建一个基类,所有可能的成员都必须从该基类继承并保留所有模板。

    你基本上要介绍existential types,这在C++中是不支持的。

    【讨论】:

      【解决方案4】:

      在当前标准中是不可能的。我相信通过假设的“概念”(?)成为 C++0x 中的标准是可能的。

      【讨论】:

      • 概念不再是 C++0x 的一部分
      【解决方案5】:

      好吧,我想你可以定义一个在模板参数上调用Init()Destroy() 的方法,并以某种方式在调试模式下调用此方法。

      或者,您可以定义一个接口,并在模板实现中转换为该接口。这也可以在发布模式下禁用。

      【讨论】:

        【解决方案6】:

        是的,你可以。

        然而,它相当复杂(高级模板元编程) 整个概念建立在 C++ 模板中的“替换失败不是错误 (SFINAE)”质量之上。

        本质上,您可以使用 'template &lt;typename T, void (T::*)()&gt;' 之类的模板,并在向量中使用 &lt;T, T::Init&gt; 进行实例化。除非存在替换,否则您将收到替换错误(此处使用 SFINAE 原则,因为大多数时候您希望使用构造函数)。

        当然,这是过于简化的描述。很抱歉,我目前无法提供更好的,但您可能想看看this discussion。搜索 has_member 和 is_call_possible。

        希望对你有帮助。
        奥伦

        【讨论】:

          【解决方案7】:

          实际上恰恰相反:如果您的模板需要Init()Destroy(),则不可能用没有这两个的任何类型来实例化它。

          在这方面,模板的唯一问题是要求是隐式的(即,模板不会在模板中需要的地方编译),而不是显式的(即编译器会告诉你此时缺少什么实例化)。概念旨在解决这个问题,但它们是 taken out of the next standard 不久前。

          【讨论】:

            猜你喜欢
            • 2015-05-08
            • 1970-01-01
            • 1970-01-01
            • 2019-08-29
            • 1970-01-01
            • 2012-12-04
            • 2015-06-14
            • 2013-05-22
            • 1970-01-01
            相关资源
            最近更新 更多