使用声明和前置声明之间的冲突

4

让我们和牛头犬一起散步 :)

假设我有一个命名空间Street::House(在命名空间Street内),其中声明了Bulldog类(假设它在House/Bulldog.hpp中):

namespace Street {
namespace House {
class Bulldog {};
}
}

然后,我有一个名为 Bulldog.hpp 的文件:
#include "House/Bulldog.hpp"    

namespace Street {
using House::Bulldog;
}

注意以下内容:我正在使用using声明将Street::House::Bulldog的定义注入到命名空间Street中,并将其作为Street::Bulldog

然后,在 Owner.hpp 中,类Bulldog前向声明

namespace Street {
class Bulldog;

class Owner {
  Bulldog* bulldog;
};
}

最后,我有了 Owner.cpp 文件:
#include "Owner.hpp"
#include "Bulldog.hpp"

namespace Street {
// Implementation of Owner...
}

在Owner.cpp中出现编译错误:
错误:'Bulldog'在此范围内已经声明过
这种现象的自然解释似乎是C ++将这两个Bulldog类视为不同,但为什么呢?在这种情况下,我没有看到任何歧义,即如果编译器正确实现它,它实际上可以工作。
你能提出哪些解决方法?我能想到的一个方法是从Owner.hpp中简单地删除Bulldog的前向声明,并将#include“Bulldog.hpp”从Owner.cpp移动到Owner.hpp。然而,这将导致精确的包含,而不是前向声明。

Clang有一个更详细的错误信息:error: target of using declaration conflicts with declaration already in scope - Lily Ballard
这不是编译器的错误,注入的声明不会像那样工作。 - Stephen Lin
2个回答

3

看起来您可以通过更改Bulldog.hpp文件来解决此问题。

namespace Street {
    namespace House {
        class Bulldog;
    }
    using House::Bulldog;

    // ...
}

这对我在 Clang 中运作良好。


@Haroogan,它运行良好http://ideone.com/hGNbp1(是的,有些东西已经改变了,“using House :: Bulldog”不等同于在“Street”中声明“Bulldog”) - Stephen Lin
2
@Haroogan,你的原始代码之所以不起作用,是因为和(this)一样的原因,创建一个新名称并不会改变原来的名称;如果class Bulldog实际上不是一个类而是一个using-injected或者typedef-ed类型名称,那么你就不能前向声明它。这是两个不同的命名空间(标签名称和类型);C++只是自动地让你使用标签名称作为类型,而不像C语言那样,但它们仍然是不同的。 - Stephen Lin
@Haroogan 不是很准确,你可以使用模板来混淆名称绑定,但每个翻译单元都需要知道命名空间名称实际上是在哪里声明的(而不仅仅是注入的)...否则链接器将不得不理解比它更多的C++;命名空间基本上只是一种以一致的方式编码名称的方法。 - Stephen Lin
没错。我会让它保持开放几天。也许有人能想出一个聪明的技巧或者记得 Boost 中使用的技术是什么(因为我完全忘记了在哪个模块中看到它)。 - Alexander Shukaev
1
@Haroogan:由于链接的方式,我不认为有任何技术可以完全消除通过检查头文件来确定 Bulldog 命名空间来源的能力。 - Lily Ballard
显示剩余4条评论

1

Owner.hpp 是您编写的:

namespace Street {
class Bulldog;

class Owner {
  Bulldog* bulldog;
};
}

应该是:

本应该是

namespace Street {
namespace House {
class Bulldog;
}

class Owner {
  House::Bulldog* bulldog;
};
}

你不小心前向声明了一个不存在的类 Street::Bulldog,而实际上应该前向声明存在的类 Street::House::Bulldog。因此,Owner.cpp 中对 Bulldog* 的实现出现歧义。编译器无法确定你是在引用前向声明的 Street::Bulldog 还是已经声明且定义过(虽然编译器并不关心这个)的 Street::House::Bulldog

由于你想要前向声明的类是 Street::House::Bulldog,你需要在 .hpp 文件中的声明中包含第二个命名空间 House


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