首页 > 解决方案 > 为什么在以下示例中我需要在 block_cache.h 之前包含 block_cache_key.h 和 block.h?

问题描述

通常我们会使用标准类型作为键和值std::unordered_map<key, value>。但是现在我需要自定义我自己的键和值类。

关键类定义block_cache_key.h如下:

#ifndef BLOCK_CACHE_KEY_H_
#define BLOCK_CACHE_KEY_H_

#include <functional>

namespace wcg{
class BlockCacheKey{
public:
    BlockCacheKey(const std::string &name, int64_t offset) : name_(name), offset_(offset) {}

    bool operator==(const BlockCacheKey &other) const{
        return offset_ == other.offset_ && name_ == other.name_;
    }

    const std::string &name() const{
        return name_;
    }

    const int64_t offset() const{
        return offset_;
    }

    std::string to_string() const{
        return name_ + "_" + std::to_string(offset_);
    }

private:
    std::string name_;
    int64_t offset_;
};
}

namespace std{
    template <>
    class hash<wcg::BlockCacheKey>{
    public:
        size_t operator()(const wcg::BlockCacheKey &key) const{
            return std::hash<std::string>()(key.name()) ^ (std::hash<int64_t>()(key.offset()));
        }
    };
}

#endif

值类定义block.h如下:

#ifndef BLOCK_H_
#define BLOCK_H_

namespace wcg{
class Block{
public:
    Block() {}
};
}

#endif

使用的类std::unorded_map定义block_cache.h如下:

#ifndef BLOCK_CACHE_H_
#define BLOCK_CACHE_H_

#include <iostream>
#include <memory>
#include <unordered_map>

namespace wcg{

class BlockCacheKey;
class Block;

class BlockCache{
public:
    BlockCache() : count_(0) {
        std::cout << "initial stats: " << to_string() << std::endl;
    }

    ~BlockCache(){
        std::cout << "final stats: " << to_string() << std::endl;
    }

    void CacheBlock(const BlockCacheKey &key, std::shared_ptr<Block> block){
        std::cout << "cache put: " << key.to_string() << std::endl;
        map_[key] = block;
        count_++;
    }

    std::shared_ptr<Block> GetBlock(const BlockCacheKey &key){
        auto pos = map_.find(key);
        if(pos != map_.end()){
            std::cout << "cache get: " << key.to_string() << std::endl;
            return pos->second;
        }
        return nullptr;
    }

    std::string to_string() const{
        return "block count: " + std::to_string(count_);
    }

private:
    std::unordered_map<BlockCacheKey, std::shared_ptr<Block>> map_;
    int count_;
};
}

#endif

主函数定义main.cpp如下:

#include <memory>

#include "block_cache_key.h"
#include "block.h"
#include "block_cache.h"

// g++ -std=c++11 -g -Wall main.cpp

int main(int argc, char* argv[]){
    wcg::BlockCache bc;
    return 0;
}

最后是什么时候#include "block_cache.h",编译就OK了。但是当#include "block_cache.h"#include "block_cache_key.h"and 之前#include "block.h",编译会失败并且错误信息非常难看,很难理解。

我已经知道的std::unordered_map是一个模板。GCC 编译时,首先会对包含部分进行预处理,即全部展开。这给我的印象是包含顺序不是问题。

最后要注意的是,我不想包含block_cache_key.hblock.hblock_cache.h

部分编译错误信息:(整个错误信息在https://paste.ubuntu.com/p/VyrWKCTG4q/

In file included from /usr/include/c++/7/bits/hashtable.h:35:0,
                 from /usr/include/c++/7/unordered_map:47,
                 from block_cache.h:13,
                 from main.cpp:5:
/usr/include/c++/7/bits/hashtable_policy.h: In instantiation of 'struct std::__detail::__is_noexcept_hash<wcg::BlockCacheKey, std::hash<wcg::BlockCacheKey> >':
/usr/include/c++/7/type_traits:143:12:   required from 'struct std::__and_<std::__is_fast_hash<std::hash<wcg::BlockCacheKey> >, std::__detail::__is_noexcept_hash<wcg::BlockCacheKey, std::hash<wcg::BlockCacheKey> > >'
/usr/include/c++/7/type_traits:154:31:   required from 'struct std::__not_<std::__and_<std::__is_fast_hash<std::hash<wcg::BlockCacheKey> >, std::__detail::__is_noexcept_hash<wcg::BlockCacheKey, std::hash<wcg::BlockCacheKey> > > >'
/usr/include/c++/7/bits/unordered_map.h:103:66:   required from 'class std::unordered_map<wcg::BlockCacheKey, std::shared_ptr<wcg::Block> >'
block_cache.h:50:60:   required from here
/usr/include/c++/7/bits/hashtable_policy.h:87:34: error: no match for call to '(const std::hash<wcg::BlockCacheKey>) (const wcg::BlockCacheKey&)'
  noexcept(declval<const _Hash&>()(declval<const _Key&>()))>
           ~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~
In file included from /usr/include/c++/7/bits/move.h:54:0,
                 from /usr/include/c++/7/bits/stl_pair.h:59,
                 from /usr/include/c++/7/bits/stl_algobase.h:64,
                 from /usr/include/c++/7/memory:62,
                 from main.cpp:3:
/usr/include/c++/7/type_traits: In instantiation of 'struct std::__not_<std::__and_<std::__is_fast_hash<std::hash<wcg::BlockCacheKey> >, std::__detail::__is_noexcept_hash<wcg::BlockCacheKey, std::hash<wcg::BlockCacheKey> > > >':
/usr/include/c++/7/bits/unordered_map.h:103:66:   required from 'class std::unordered_map<wcg::BlockCacheKey, std::shared_ptr<wcg::Block> >'
block_cache.h:50:60:   required from here
/usr/include/c++/7/type_traits:154:31: error: 'value' is not a member of 'std::__and_<std::__is_fast_hash<std::hash<wcg::BlockCacheKey> >, std::__detail::__is_noexcept_hash<wcg::BlockCacheKey, std::hash<wcg::BlockCacheKey> > >'
     : public __bool_constant<!bool(_Pp::value)>
                               ^~~~~~~~~~~~~~~~
In file included from /usr/include/c++/7/unordered_map:48:0,
                 from block_cache.h:13,
                 from main.cpp:5:
/usr/include/c++/7/bits/unordered_map.h: In instantiation of 'class std::unordered_map<wcg::BlockCacheKey, std::shared_ptr<wcg::Block> >':
block_cache.h:50:60:   required from here
/usr/include/c++/7/bits/unordered_map.h:103:66: error: 'value' is not a member of 'std::__not_<std::__and_<std::__is_fast_hash<std::hash<wcg::BlockCacheKey> >, std::__detail::__is_noexcept_hash<wcg::BlockCacheKey, std::hash<wcg::BlockCacheKey> > > >'
       typedef __umap_hashtable<_Key, _Tp, _Hash, _Pred, _Alloc>  _Hashtable;
                                                                  ^~~~~~~~~~
/usr/include/c++/7/bits/unordered_map.h:110:45: error: 'value' is not a member of 'std::__not_<std::__and_<std::__is_fast_hash<std::hash<wcg::BlockCacheKey> >, std::__detail::__is_noexcept_hash<wcg::BlockCacheKey, std::hash<wcg::BlockCacheKey> > > >'
       typedef typename _Hashtable::key_type key_type;

标签: c++unordered-map

解决方案


让我们看看BlockCache.h。您向编译器保证存在Block并且BlockCacheKey最终将被定义——此时它们是不完整的类型。只要您不以要求它们完整的方式使用它们就可以并且效果很好std::shared_ptr形成不完整类型的引用、指针甚至s 都可以。请参阅此处以获取您不能对它们做的事情的列表。

您通过使用BlockCacheKey(and Block) in std::unordered_map<BlockCacheKey, std::shared_ptr<Block>>- 来实例化std::unordered_map<Key, Value>模板(这是声明该类型的成员所必需的)违反了这一点,KeyandValue类型必须是完整的。

想象一下,这sizeof(std::unordered_map<Key, Value>)取决于sizeof(Key)(这在其权利范围内)。只有前向声明Key,sizeof(Key)将是未知的,因此sizeof(std::unordered_map<Key, Value>)将是未知的,因此您的大小BlockCache将是未知的(即使在您定义它之后!)。编译器无法使用它,这就是为什么不允许你这样做。

最后要注意的是,我不想在 block_cache.h 中包含 block_cache_key.h 和 block.h

这是不可能的(正如您所观察到的,不需要用户提供时髦的包含订单)。要实例化,必须知道std::map<BlockCacheKey, Whatever>的定义,您只能从包含其标题中获得。BlockCacheKey我确实相信您打算用作地图的值类型的类型也是Block如此。std::shared_ptr<Block>


推荐阅读