散列多态类型是正确的方式

我有一个使用Howard Hinnant方法实现的哈希过程(基于hash_append重载的泛型哈希)。

该方法的目的是为了“记忆”计算结果而创建类的散列(参见本答案的结尾),所以我正面临一些问题。 特别是,请考虑以下可能的需要进行散列的Input类:

struct A {
    virtual int do_stuff() const = 0;
    virtual ~A(); 
};
struct B: A {
    int do_stuff() const override { return 0; }
};
struct C: A {
    const int u;
    int do_stuff() const override { return u; }
};

struct Input {
    A const& a; // store a reference to an instance of B or C
};

现在,如果我想散列Input ,我会有这样的:

template <class HashAlgorithm>
void hash_append(HashAlgorithm& h, Input const& input) {
    hash_append(h, typeid(input));
    hash_append(h, typeid(input.a));
}

所以我需要为A重载hash_append

template <class HashAlgorithm>
void hash_append(HashAlgorithm& h, A const& a) {
    hash_append(h, typeid(a)); 
}

这里的问题是,根据a的运行时类型,我需要为散列添加额外的信息,例如,对于C我需要添加u

我想过以下解决方案(和缺点):

  • 将一个虚拟方法添加到A ,该方法返回可添加到typeid()散列的特定值,但是:

  • 这意味着在A中添加一个与A的目的无关的方法,因此我不太喜欢这个想法(特别是因为我有多个A类的类)。
  • 这打破了hash_append的概念,因为该方法将对所有继承类具有唯一的返回类型。
  • hash_append里面做一堆dynamic_cast

  • 我发现这非常丑陋......特别是如果我有多个类似于A类;
  • 这很容易出错:如果有人添加了A的新子项并且不在hash_append内添加dynamic_cast。
  • 有没有办法散列多态类型,而不必修改类型本身或依赖一堆dynamic_cast


    最后的目标是能够记忆一些重要功能的结果。 我们来概述我的应用程序的基本结构:

    struct Input { };
    struct Result { };
    
    Result solve(Input const&);
    

    solve函数的计算量很大,所以我想用前面的计算结果保存在使用Input s的散列的文件中,例如:

    // depends on hash_append
    std::string hash(Input const&);
    
    Result load_or_solve(Input const& input) {
        auto h = hash(input);
        Result result;
        if (exists(h)) { // if result exists, load it
            result = load(h);
        }
        else { // otherwize, solve + store
            result = solve(input);
            store(h, result);
        }
        return result;
    }
    

    loadstore方法将加载和存储文件结果,目标是在不同运行之间记忆解决方案。

    如果您对如何在不处理上述问题时记忆这些结果有所建议,我很乐意阅读它们。


    您可以在Ahash_append版本中使用双重调度,并将请求转发到适当的版本(即BC )。 缺点是你必须添加样板才能接受访问者,我不能说你是否可以接受。
    下面是一些应该说明这个想法的代码:

    struct B;
    struct C;
    
    struct Visitor {
        virtual void visit(const B &) = 0;
        virtual void visit(const C &) = 0;
    };
    
    template<typename T, typename... O>
    struct HashVisitor: T, HashVisitor<O...> {
        template<typename U>
        std::enable_if_t<std::is_same<T, U>::value> tryVisit(const U &u) {
            T::operator()(u);
        }
    
        template<typename U>
        std::enable_if_t<not std::is_same<T, U>::value> tryVisit(const U &u) {
            HashVisitor<O...>::visit(u);
        }
    
        void visit(const B &b) override { tryVisit<B>(b); }
        void visit(const C &c) override { tryVisit<C>(c); }
    };
    
    template<>
    struct HashVisitor<>: Visitor {};
    
    template<typename... F
    auto factory(F&&... f) {
        return HashVisitor<std::decay_t<F>>{std::forward<F>(f)...};
    }
    
    struct A {
        virtual void accept(Visitor &) = 0;
        virtual int do_stuff() const = 0;
        virtual ~A();
    };
    
    struct B: A {
        void accept(Visitor &v) override { v.visit(*this); }
        int do_stuff() const override { return 0; }
    };
    
    struct C: A {
        const int u;
        void accept(Visitor &v) override { v.visit(*this); }
        int do_stuff() const override { return u; }
    };
    
    template <class HashAlgorithm>
    void hash_append(HashAlgorithm &, const B &) {
        // do something
    }
    
    template <class HashAlgorithm>
    void hash_append(HashAlgorithm &, const C &) {
        // do something
    }
    
    template <class HashAlgorithm>
    void hash_append(HashAlgorithm &h, const A &a) {
        auto vis = factory(
            [&h](const B &b){ hash_append(h, b); },
            [&h](const C &c){ hash_append(h, c); }
        );
    
        a.accept(vis);
    }
    
    链接地址: http://www.djcxy.com/p/97083.html

    上一篇: Hashing polymorphic type the proper way

    下一篇: Use a Library in the processor's generated classes