C++ 转发声明会在代码重构后导致错误

C++ 转发声明会在代码重构后导致错误,c++,namespaces,refactoring,forward-declaration,using-directives,C++,Namespaces,Refactoring,Forward Declaration,Using Directives,我最初的班级结构类似于: //def.h namespace A { struct X {}; } 并在需要时提交声明: //file that needs forward declarations namespace A { struct X; } 经过一些重构后,X被移动到另一个名称空间,但为了保持旧代码“工作”,使用了指令: //def.h namespace B { struct X {}; } namespace A { using ::B::X; } 现在,我

我最初的班级结构类似于:

//def.h
namespace A
{
   struct X {};
}
并在需要时提交声明:

//file that needs forward declarations
namespace A { struct X; }
经过一些重构后,
X
被移动到另一个名称空间,但为了保持旧代码“工作”,使用了
指令:

//def.h
namespace B
{
   struct X {};
}
namespace A
{
   using ::B::X;
}
现在,我们可以访问同一个类,保留旧语法
A::X
,但是转发声明会导致错误。第二个问题是,我收到的错误消息没有指向转发声明的位置,查找/替换转发声明非常耗时

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

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

在我看来,使用
根本不应该存在,所有使用
X
的代码都应该重构以适应新的名称空间(这是一个解决方案),但不幸的是,这不是一个选项


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

最好的方法是修复代码

您可以通过两个步骤完成此操作:

  • 修复所有正向声明
  • 使用::B::X删除

  • 我意识到这更多的是关于新代码而不是重构现有代码,但我喜欢在这种情况下使用一个名为
    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>
    

    因此,在名称空间中同时使用具有相同名称的直接声明和使用声明的有效性取决于顺序。因此,在fwd头文件中使用单个声明非常方便。

    我猜
    fwd.h
    不是
    def_fwd.h
    ,也就是说,一个头,其全部目的是向前声明一些其他头文件名?@K-ballo这只是一个名称,向前声明是每个实现文件。嗯,这就泄露了我处理这件事的建议。取自标准库(参见
    iosfwd
    )、Boost(到处都是)等。是
    X
    移动到现有名称空间
    B
    还是
    名称空间A
    重命名为
    B
    ?在后一种情况下,是否可以使用命名空间别名?@rhalbersma existing namespace-
    A
    仍然存在。让结构声明和类型定义共享一个名称仍然是一个错误。typedef也不会影响ADL。@SebastianRedl Tnx,更新以反映使用指令和typedef/使用声明之间的细微差别。+1作为最后一个细节-我在自己解决问题时看到了这一点,但无法解释原因。@LuchianGrigore我很高兴找到精确的细节。该标准在第3.4节开始时非常有趣:“名称查找规则统一适用于所有名称[…]”,然后是14页(!)的非限定、限定、使用指令、使用声明和其他复杂细节;-)是的,我也这么认为,这就是我所做的(没有使用
    指令删除
    ,这不是一个选项)。
    
    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!)
    }