【问题标题】:Inherit interface class only if template type match concept仅当模板类型匹配概念时才继承接口类
【发布时间】:2022-01-12 10:52:22
【问题描述】:

我有一个带有纯虚拟readwrite 函数的ISerializable 接口。我还有一个List<T> 课程。所以,我的问题是:只有当T 也继承自ISerializable 时,我才能从ISerializable 继承我的List<T> 吗?

用法示例:

#include <array>
#include <iostream>

class ISerializable
{
public:
    virtual void read(std::istream& stream) = 0;
    virtual void write(std::ostream& stream) = 0;
};

class A : public ISerializable {
public:
    void read(std::istream& stream) override {}
    void write(std::ostream& stream) override {}
};
class B {};

template<typename T>
class List : public ISerializable
{
public:
    void read(std::istream& stream) override
    {
        for (int i = 0; i < items.size(); i++)
        {
            items[i].read(stream);
        }
    }

    void write(std::ostream& stream) override
    {
        for (int i = 0; i < items.size(); i++)
        {
            items[i].write(stream);
        }
    }

private:
    std::array<T, 10> items;
};

int main()
{
    List<A> list_a;
    //List<B> list_b; <- error here
}

【问题讨论】:

  • @kiner_shah - “仅当”位呢?
  • 简单的解决方案可能是template &lt;typename T&gt; class List : public std::conditional_t&lt;std::is_base_of_v&lt;ISerializable,T&gt;, ISerializable, ???&gt;...。唯一的问题是,在错误条件的情况下,您需要从其他一些基础派生,例如一些帮助器空 INonSerializable 类。
  • @StoryTeller-UnslanderMonica,实际上我不确定这个问题,我试图确认我的理解是否正确。似乎不正确:-)
  • @kiner_shah 之类的。我扩展了你的 sn-p:godbolt.org/z/73MhjbWxo

标签: c++ templates inheritance c++20 c++-concepts


【解决方案1】:

你确实可以在 C++20 中设置类似的东西。

template<typename T>
class List : public ListCommon<T> {
};

template<std::derived_from<ISerializable> S>
class List<S> : public ListCommon<S>, public ISerializable {
  // Implement the serialization
};

只有在从ISerializable 派生的类型的附加约束条件下(通过标准std::derived_from 概念检查),可序列化类型的特化才可行。

ListCommon 是我放置常见实现细节的地方,以避免在专门化时重复它们。

【讨论】:

  • 如果你只有一个你想有条件地支持的接口,这很有效,但如果有多个接口就会变得笨拙
  • 作为一种选择,也许,但正如 Caleth 所说,当有更多这样的接口时,它会变得复杂
  • @Espeon - 多个接口使这成为一种不同的球类游戏(这不是您真正要求的,是吗?)ListCommon 是一种提取通用实现细节以重用的方法ListSer可以是另一个基地。但是你需要更多超出你所提出的范围的样板。
  • 我相信这是有人进来并指出他们应该用 Rust 重写它,因为impl&lt;T&gt; Serialize for List&lt;T&gt; where T: Serialize { ... } 非常时髦。
  • @Barry - 我一年前开始使用 Rust,现在我需要回到它,因为那个评论:) 你知道的越多......
【解决方案2】:

好的,如果您已经有很多不想重构和/或复制的类,则此解决方案适合您。这里的缺点是 IDE 有时无法为您提供帮助。但是,由于它暗示了概念的主要特征之一,它仍然是合法的。小心点!

这里我们有一个明确要求实现某些功能的接口和一个也需要实现相同功能的概念。更喜欢界面而不是概念!如果您继承接口,IDE 将协助您进行函数重载。如果您想稍后将接口用作参数类型,则需要概念(参见实用程序类示例)。这是duck typing 的示例。

class ISerializable
{
public:
    virtual void write_to_stream(std::ostream& stream) const = 0;
    virtual void read_from_stream(std::istream& stream) = 0;
};

template<typename T>
concept ImplicitlySerializable = requires(const T a1, T a2, std::ostream os, std::istream is)
{
    a1.write_to_stream(os);
    a2.read_from_stream(is);
};

template<typename T>
concept Serializable = std::derived_from<T, ISerializable> || ImplicitlySerializable<T>;

一些实用函数来处理任何匹配概念的对象

class StreamUtils
{
public:
    template<Serializable T>
    static void write(std::ostream& stream, const T& value)
    {
        value.write_to_stream(stream);
    }

    template<Serializable T>
    static void read(std::istream& stream, T& to)
    {
        to.read_from_stream(stream);
    }
};

使用示例:

// Interface usage
class Animal : public ISerializable
{
    void write_to_stream(std::ostream& stream) override
    {
        // serialization code here
    }

    void read_from_stream(std::istream& stream) override
    {
        // deserialization code here
    }
}

// Concept usage
template<typename T>
class List
{
    void write_to_stream(std::ostream& stream) const requires Serializable<T>
    {
        // serialization code here
    }

    void read_from_stream(std::istream& stream) requires Serializable<T>
    {
        // deserialization code here
    }
{

int main()
{
    std::ofstream file("my_file.bin", std::ios::out | std::ios:binary);
    StreamUtils::write(file, Animal());
    StreamUtils::write(file, List<Animal>());
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-05
    • 2016-12-14
    • 1970-01-01
    • 2020-08-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多