【问题标题】:Forward declarations cause errors after code refactor代码重构后前向声明导致错误
【发布时间】:2012-12-23 12:48:06
【问题描述】:

我原来的类结构类似于:

//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 的代码都应该重构以适应新的命名空间(这是一种解决方案),但不幸的是,这不是一个选项。

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

【问题讨论】:

  • 我猜fwd.h 不是def_fwd.h,也就是说,一个头文件的全部意图是转发声明一些其他的头文件名称?
  • @K-ballo 这只是一个名称,前向声明是每个实现文件。
  • 嗯,这放弃了我处理这个问题的建议。取自 Standard Library(参见 iosfwd)、Boost(到处都是)等。人。
  • X 是否已移至现有命名空间 Bnamespace A 已重命名为 B?在后一种情况下,您可以使用命名空间别名吗?
  • @rhalbersma 现有命名空间 - A 仍然存在。

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


【解决方案1】:

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

NOTE2:在问题的 cmets 中,给出了一个 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) 用于通过 X 中的 using 指令指定的所有命名空间 Ni 和 它的内联命名空间集。

这解释了以下棘手的歧义

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

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

【讨论】:

  • struct 声明和 typedef 共享名称仍然是错误的。并且 typedef 也不影响 ADL。
  • @SebastianRedl Tnx,已更新以反映 using 指令和 typedef/using 声明之间的细微差别。
  • +1 表示最后的微妙之处 - 我在自己解决问题时看到了这一点,但无法解释原因。
  • @LuchianGrigore 找到精确的细节让我很开心。该标准在第 3.4 节开始非常有趣:“名称查找规则统一适用于所有名称 [...]”,然后是 14(!) 页的不合格、合格、使用指令、使用声明和其他复杂细节; -)
【解决方案2】:

最好的办法是修复代码。

您可以分两步完成:

  1. 修复所有前向声明
  2. 删除using ::B::X;

【讨论】:

  • 是的,我也是这么想的,这就是我所做的(不删除using 指令,这不是一个选项)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-08-17
相关资源
最近更新 更多