基于方法或自由函数的存在的模板构造函数解析

Template constructor resolution based on existence of method or free-function

问题

受到 Sean Parent 的 "Runtime Polymorphism" 的启发,我实现了一个 Serializable class,它使用类型擦除来调度 Serializable::serialize(...)obj.serialize(...),其中 obj 是一个包装对象。

struct Serializable
{
    template <typename T>
    Serializable(T obj)
        : m_self(std::make_unique<Model<T> >(std::move(obj))) {}

    /// Writes itself to a write storage
    void serialize(Storage& outStream)
    { return m_self->serialize(outStream); }  
private:
    struct Concept
    {
        virtual ~Concept() = default;
        virtual void serialize(Storage& outStream) = 0;
    };

    template <typename T>
    class Model final : public Concept
    {
    public:
        Model(T x) : m_data(std::move(x)) {}
    private:
        void serialize(Storage& outStream) override
        { m_data.serialize(outStream); } 
    private:
        T m_data;
    }; 
private:
    std::unique_ptr<Concept> m_self;
};

现在我想用另一个模型 class 扩展 Serializable,它将 Serializable::serialize(...) 分派给一个以 obj 作为参数的自由函数:Serializable::serialize(...)serialize(obj, ...)

然后我想要 Serializable 的模板构造函数通过检查 T::serialize(...)serialize(const T&, ...)

的存在来决定使用哪个模型

问题

是否可以通过任何方式(例如 SFINAE)自动构建 Serializable,以便它尽可能使用方法序列化,否则使用自由函数序列化?

随意使用 C++17 之前的任何 C++ 标准。

您可以设计自己的特征来查明 class 是否具有正确的 serialize 成员。有几种方法可以做到,这是其中一种:

template <class T, class = void>
struct HasMemberSerialize : std::false_type
{};

template <class T>
struct HasMemberSerialize<T, std::void_t<decltype(std::declval<T>().serialize(std::declval<Storage&>()))>> : std::true_type
{};

[Live example]

然后,将新的模板参数添加到 Model 并使用特征查找其参数:

struct Serializable
{
    template <typename T>
    Serializable(T obj)
        : m_self(std::make_unique<Model<T, HasMemberSerialize<T>::value> >(std::move(obj))) {}

    /// Writes itself to a write storage
    void serialize(Storage& outStream)
    { return m_self->serialize(outStream); }  
private:
    struct Concept
    {
        virtual ~Concept() = default;
        virtual void serialize(Storage& outStream) = 0;
    };

    template <typename T, bool Member>
    class Model;
private:
    std::unique_ptr<Concept> m_self;
};

template <typename T>
class Serializable::Model<T, true> final : public Serializable::Concept
{
public:
    Model(T x) : m_data(std::move(x)) {}
private:
    void serialize(Storage& outStream) override
    { m_data.serialize(outStream); } 
private:
    T m_data;
};

template <typename T>
class Serializable::Model<T, false> final : public Serializable::Concept
{
public:
    Model(T x) : m_data(std::move(x)) {}
private:
    void serialize(Storage& outStream) override
    { serialize(m_data, outStream); } 
private:
    T m_data;
};