前向声明在代码重构后导致错误

时间:2013-01-09 13:00:10

标签: c++ namespaces refactoring forward-declaration using-directives

我原来的班级结构类似于:

//def.h
namespace A
{
   struct X {};
}

并在需要时转发声明:

//file that needs forward declarations
namespace A { struct X; }

经过一些重构后,X被移到了另一个名称空间,但为了保持旧代码“工作”using指令的使用:

//def.h
namespace B
{
   struct X {};
}
namespace A
{
   using ::B::X;
}

现在我们可以访问保留旧语法A::X的同一个类,但前向声明会导致错误。第二个问题是我得到的错误消息并未指向前向声明的位置,并且查找/替换前向声明非常耗时。

现在我解决了问题(困难的方法)。

处理这种情况的最佳方法是什么?

IMO,using根本不应该存在,并且所有使用X的代码都应该被重构以容纳新的命名空间(这是一个解决方案),但不幸的是,这不是一个选项。

实际代码要复杂得多,这是一个简化的例子。

2 个答案:

答案 0 :(得分:4)

我意识到这更多是关于新代码而不是重构现有代码,但我喜欢在这种情况下使用名为X_fwd.hpp的特殊标头。

// X_def.hpp
namespace B
{
   struct X {};
}
namespace A
{
   // NOT: using namespace B; // does not participate in ADL!      
   typedef ::B::X X;  // OR: using ::B::X;
}

// X_fwd.hpp
namespace A { struct X; }

// some file needing declaration of X
#include <X_fwd.hpp>

这使得查找前向声明变得容易得多,并且在事后也更容易更改它们,因为更改仅在一个地方被隔离(DRY ...)。

NOTE1 :AFAIK,使用Peter Wood对typedefusing声明的回答之间没有技术差异。请注意,using指令using namespace B;可能会导致问题,因为 Argument-Dependent-Lookup 会忽略这些指令。更糟糕的是,你的一些代码甚至可以默默地调用错误的函数重载,因为你不再需要新的命名空间B

NOTE2 :在对问题的评论中,给出了Ideone示例。这很好地说明了名称空间中名称查找的细微之处:引用草案Standard 3.4.3.2命名空间成员[namespace.qual] ,第2节

  

对于命名空间X和名称m,命名空间限定的查找集S(X,   m)定义如下:设S'(X,m)为所有声明的集合   X中的m和X的内联命名空间集(7.3.1)。如果S'(X,m)是   非空,S(X,m)是S'(X,m);否则,S(X,m)是联合的   所有命名空间的S(Ni,m)Ni由X和中的using指令指定   它的内联命名空间集。

这解释了以下棘手的歧义

namespace A
{
    struct X1{};
    struct X2{};
}

namespace B
{
    using A::X1;    // OK: lookup-set is both namespace A and B, and a single unique name is found (at this point!)
    struct X1;      // OK: lookup-set is namespace B, and a single unique name is found

    struct X2;      // OK: lookup-set is namespace B, and a single unique name is found
    using A::X2;    // error: lookup-set is both namespace A and B, and no unique name is found (at this point!)
}

因此,在命名空间内同时具有直接声明和using声明的有效性依赖于顺序。因此,在fwd头文件中单个声明的便利性。

答案 1 :(得分:1)

最好的方法是修复代码。

您可以分两步完成:

  1. 修正所有转发声明
  2. 删除using ::B::X;