【问题标题】:C++ template specialisation for abstract base class抽象基类的 C++ 模板特化
【发布时间】:2026-01-28 13:50:02
【问题描述】:

我想写一个Settings类来加载和存储各种类型的设置:

template <typename T>
class Setting {
    std::string m_name;
    T m_defaultValue;

    T load() {
        if(backend.contains(m_name))
            return backend.load<T>(m_name);
        return m_defaultValue;
    }

    void save(const T &value) {
        backend.save(m_name, value);
    }
}

backend 有一些基本类型的实现,但不是我自己的。所以我需要我的类型的模板专业化。我的想法是创建一个Serializable 类并从中派生我的类:

class Serializable {
public:
    virtual std::string serialize() const = 0;
    virtual void deserialize(const std::string &data) = 0;
}

现在我需要在 Setting 类中对 loadsave 进行模板特化,以便在 Serializable 中使用 serializedeserialize 函数:

template<>
void Setting<Serializable>::load()
{
    if(backend.contains(m_name)) {
        Serializable s;
        s.deserialize(backend.load<std::string>(m_name);
        return s;
    }
    return m_defaultValue;
}

template<>
void Setting<Serializable>::save(const Serializable &value)
{
    backend.save(m_name, value.serialize());
}

显然这不起作用,因为在Setting&lt;Serializable&gt;::load() 中我无法实例化Serializable s。但是即使我暂时忽略load,它也不起作用,因为它cannot declare field ‘Setting&lt;Serializable&gt;::m_defaultValue’ to be of abstract type ‘Serializable’

有没有办法为Serializable 的所有子类型编写不同的实现,但仍使用模板参数T

【问题讨论】:

  • 您使用的是哪个版本的 c++?使用 c++17 你可以使用if constexpr。否则,更好的选择可能是部分模板专业化。但是,您不能只专门化一种方法,您需要专门化整个类。
  • @super 谢谢,它适用于if constexpr (std::is_convertible_v&lt;T&amp;, Serializable&amp;&gt;)

标签: c++ template-specialization abstract-base-class


【解决方案1】:

您可以通过创建一个专门版本的 Setting 来做到这一点,该版本仅在设置类型继承自 Serializable 时才可用。

通过对std::enable_if 使用部分模板特化,您可以创建一个单独的设置类,该类仅在设置类型实现Serializable 时使用。

例如:

// Normal Setting class, for the primitive types your backend supports
template <typename T, typename _ = void>
class Setting {
    std::string m_name;
    T m_defaultValue;

public:
    T load() {
        if(backend.contains(m_name))
            return backend.load<T>(m_name);
        return m_defaultValue;
    }

    void save(const T &value) {
        backend.save(m_name, value);
    }
};

// specialized Setting class that will apply to all classes that implement Serializable
template<typename T>
class Setting<T, std::enable_if_t<std::is_base_of_v<Serializable, T>>> {
    std::string m_name;
    T m_defaultValue;

public:
    T load() {
        if(backend.contains(m_name)) {
            T s;
            s.deserialize(backend.load<std::string>(m_name));
            return s;
        }

        return m_defaultValue;
    }

    void save(const T &value) {
        backend.save(m_name, value.serialize());
    }
};

(heres an example godbolt)

【讨论】: