代码之家  ›  专栏  ›  技术社区  ›  Damir Tenishev

处理可选类成员的方法是什么?

  •  1
  • Damir Tenishev  · 技术社区  · 2 年前

    在处理大量数据(GB)时,我使用数据数组的索引。由于对数据的访问可能会导致缓存效率低下,所以我想将数组中的一些数据和索引一起缓存,这样可以显著加快通过索引的操作。

    缓存数据的数量是编译时的选择,应该包括零数量的缓存数据。我有大量的索引,所以在这种情况下,我不想为额外的空元素付费,比如 std::array 例如。

    所以,我专门制作了一个模板:

    using index_t = unsigned int;
    using lexem_t = unsigned int;
    
    template <std::size_t t_arg_cache_line_size>
    struct lexem_index_with_cache_t {
        index_t index;
        std::array<lexem_t, t_arg_cache_line_size> cache_line;
    
        constexpr std::size_t cache_line_size() const {
            return t_arg_cache_line_size;
        }
    };
    
    template<>
    struct lexem_index_with_cache_t<0> {
        index_t index;
        static std::array<lexem_t, 0> cache_line;
    
        constexpr std::size_t cache_line_size() const {
            return 0;
        }
    };
    
    std::array<lexem_t, 0> lexem_index_with_cache_t<0>::cache_line;
    

    问题是我在零大小的专业化中使用的这个破解,它利用静态成员对 cache_line 而它是空的,并且并不真正需要访问。这使我可以避免在使用此模板的函数中进行专业化,例如:

    using lexem_index_with_cache = lexem_index_with_cache_t<0>;
    
    template <typename T>
    class seq_forward_comparator_cached
    {
        const std::vector<T>& vec;
    public:
        seq_forward_comparator_cached(const std::vector<T>& vec) : vec(vec) { }
    
        bool operator() (const lexem_index_with_cache& idx1, const lexem_index_with_cache& idx2)
        {
            if (idx1.index == idx2.index) {
                return false;
            }
    
            const auto it1_cache_line = idx1.cache_line;  // This code wouldn’t compile in absence of static “hack”
            const auto it2_cache_line = idx2.cache_line;  // This code wouldn’t compile in absence of static “hack”
    
            auto res = std::lexicographical_compare_three_way(
                it1_cache_line.begin(), it1_cache_line.end(),
                it2_cache_line.begin(), it2_cache_line.end());
    
            if (res == std::strong_ordering::equal) {
                auto range1 = std::ranges::subrange(vec.begin() + idx1.index + idx1.cache_line_size(), vec.end());
                auto range2 = std::ranges::subrange(vec.begin() + idx2.index + idx2.cache_line_size(), vec.end());
    
                return std::ranges::lexicographical_compare(range1, range2);
            }
    
            return res == std::strong_ordering::less;
        }
    };
    

    当然,我可以为零大小缓存实现这个模板的另一个模板专用化,但这会导致代码重复,而且我有很多这样的功能,所以我不想全部专用化。

    在现代C++中,避免这种情况的正确方法是什么 static 另一方面,黑客攻击和可能的代码复制?

    我不确定,也许根据类型包括一些条件代码会有所帮助。

    我想避免包装访问 cache_line 到一个函数,但如果这是唯一的情况,请提供有关方法的线索。

    可编译代码为 here .

    1 回复  |  直到 2 年前
        1
  •  1
  •   Aedoro    2 年前

    我添加了一个示例,其中包含使用 if constexpr(...) 在非模板代码中。

    现在无法实际使用 at() 意外地起作用,而不是静态成员解决方案。

    #include <array>
    
    using data_type = int;
    
    template<size_t _data_size>
    class ExtendableIndex
    {
    public:
        constexpr static size_t data_size = _data_size;
    
        data_type& at(size_t idx) { return data[idx]; }
    
        size_t index;
        std::array<data_type, _data_size> data;
    };
    
    template<>
    class ExtendableIndex<0>
    {
    public:
        constexpr static size_t data_size = 0;
    
        data_type& at(size_t idx);
    
        size_t index;
    };
    
    using DefaultIndex = ExtendableIndex<0>;
    
    class DataUser
    {
    public:
    
        void process(DefaultIndex& index)
        {
            if constexpr (DefaultIndex::data_size > 0)
            {
                // auto value = index.data[0]; // -> this fails to compile
                auto value = index.at(0); // -> but this slight workaround solves the issue, `at()` is not implemented and thats OK.
            }
        }
    
        template<size_t _data_size>
        void process_template(ExtendableIndex<_data_size>& index)
        {
            if constexpr (DefaultIndex::data_size > 0)
            {
                auto value = index.data[0]; // -> this compiles even if index.data doesn't exist when 'process' is a template
            }
        }
    
    };
    
    int main()
    {
        DataUser r;
        ExtendableIndex<0> index_zero;
    
        r.process(index_zero);
        r.process_template(index_zero);
    
        ExtendableIndex<1> index_one;
        r.process_template(index_one);
    }