在通用命名空间中前向声明类(在std::hash中)

3

我正在解决一个前向声明问题。B 引用了 A,而 A 使用了一个 std::vector 来存储 B。 A 和 B 都定义在通用(无)命名空间中。

在 A 的头文件中前向声明 B 可以解决 A 中成员的问题。然而,我在同一头文件中为 A 定义了哈希函数,这会导致问题。

#include "B.h"
class B;
class A{
public:
   std::vector<B> bs; 
}

namespace std
{
template <>
struct hash<A>
{
    size_t operator()(const A& k) const
    {
        std::size_t seed = 0;
        boost::hash_combine(seed, k.foo);
        boost::hash_combine(seed, k.bar);
        for(B &b:k.bs){
            boost::hash_combine(seed, b.abc);
        }

        return seed;
    }
};
}

该函数访问B的向量,因此需要前向声明。然而,在父头文件中它并没有使用前向声明。不幸的是,我不能在std命名空间内再次前向声明它,因为这将在定义之间创建歧义。有什么想法吗?


你尝试过使用“friend”吗? - David Pulse
我没有看到 foobar 作为 A 的成员。请发布您的错误。 - R Sahu
不完整类型是第一个错误;在std命名空间中添加另一个B的前向声明会导致歧义问题。 - Chris
顺便说一下 - 我目前的解决方法是在A中添加一个成员函数,该函数从B中获取abc值,这些值是整数。这样,就不需要在哈希函数中处理类型B,但这显然是一个设计缺陷,对性能有负面影响。 - Chris
1个回答

1
您可以将hash<A>::operator()的定义移动到您的源文件中。因此:
// A.h
#include <vector>
#include <functional>

struct B;

struct A {
    std::vector<B> bs;
};

namespace std {
    template <>
    struct hash<A> {
        size_t operator()(const A& ) const;
    };
}

// A.cpp
#include "B.h"

// now, B is complete, so we can define operator()
size_t std::hash<A>::operator()(const A& k) const
{
    std::size_t seed = 0;
    boost::hash_combine(seed, k.foo);
    boost::hash_combine(seed, k.bar);
    for(const B& b : k.bs){
        boost::hash_combine(seed, b.abc);
    }

    return seed;
}

声明一个 std::vector<?>,其中 ? 是一个不完全类型,在技术上是一个不合格的程序,不需要诊断。 这是标准中的一个缺陷。 是否已经修复? - Yakk - Adam Nevraumont
1
@Yakk,我们现在有了这个:如果分配器满足分配器完整性要求17.6.3.5.1,则可以在实例化vector时使用不完整类型T。在引用vector的任何成员之前,必须使T完整。 - Barry
@Yakk [vector.overview]/3 - Barry

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接