本文介绍了代码重构后,前向声明会导致错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

//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的代码以适应新的名称空间(这是一种解决方案),但是不幸的是,这不是一个选择. /p>

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

解决方案

我意识到这更多的是关于新代码,而不是重构现有代码,但是我喜欢在这种情况下使用称为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的typedef答案和您的using声明之间没有技术上的区别.请注意,using指令using namespace B;可能会引起麻烦,因为这些指令会被 Argument-Dependent-Lookup 忽略.更糟糕的是,您的某些代码甚至可能静默地调用错误的函数重载,因为您不再使用新的命名空间B

注意2 :在对该问题的评论中,给出了一个 Ideone 示例.这很好地说明了有关命名空间中名称查找的微妙之处:引用标准,第 3.4.3.2节命名空间成员[namespace.qual] ,第2条

这解释了以下棘手的歧义

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头文件中使用单个声明很方便.

My original class structure was similar to:

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

and forward declarations where needed:

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

After some refactoring, X was moved to a different namespace, but to keep old code "working" using directives were used:

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

Now we can access the same class keeping the old syntax A::X, but the forward declarations cause errors. The second problem is that the error message I'm getting doesn't point to where the forward declarations are, and finding/replacing the forward declarations is time-consuming.

For now I fixed the problem (the hard way).

What is the best approach to deal with this situation?

IMO, the using shouldn't be there at all, and all code that uses X should be refactored to accomodate the new namespace (this is one solution), but unfortunately this isn't an option.

The actual code is a lot more complicated, this is a simplified example.

解决方案

I realize this is more about new code than about refactoring existing code, but I like using a special header called X_fwd.hpp for such cases.

// 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>

This makes it a lot easier to find forward declarations, and also to change them after the fact, because the change is isolated in one place only (DRY...).

NOTE1: AFAIK, there is no technical difference between using Peter Wood's answer of a typedef and your using declaration. Note that a using directive using namespace B; could cause trouble because these are ignored by Argument-Dependent-Lookup. Even worse, some of your code might then even silently call the wrong function overload because you are not pulling in the new namespace B anymore!

NOTE2: In the comments to the question, an Ideone example was given. This nicely illustrates a subtlety about name lookup inside namespaces: to quote from the draft Standard, section 3.4.3.2 Namespace members [namespace.qual], clause 2

This explains the following tricky ambiguity

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!)
}

So the validity of having both a direct declaration and a using-declaration with the same name inside a namespace is order-dependent. Hence the convenience of a single declaration in a fwd header file.

这篇关于代码重构后,前向声明会导致错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-22 14:09