代码重构后,正向声明会导致错误

Forward declarations cause errors after code refactor

提问人:Luchian Grigore 提问时间:1/9/2013 最后编辑:TemplateRexLuchian Grigore 更新时间:1/10/2013 访问量:331

问:

我原来的类结构类似于:

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

并在需要时转发声明:

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

经过一些重构后,被移动到不同的命名空间,但为了保持旧代码的“工作”,使用了指令:Xusing

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

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

现在我解决了这个问题(艰难的方式)。

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

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

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

C++ 命名空间 forward-declaration using-directives

评论

0赞 K-ballo 1/9/2013
我猜不是,也就是说,一个标头,其全部目的是转发声明其他一些标头名称?fwd.hdef_fwd.h
0赞 Luchian Grigore 1/9/2013
@K-ballo 它只是一个名称,forward-declarations 是每个实现文件。
0赞 K-ballo 1/9/2013
好吧,这放弃了我处理这个问题的建议。取自标准库(参见)、Boost(到处都是)等。铝。iosfwd
0赞 TemplateRex 1/9/2013
已移至现有命名空间或已重命名为 ?在后一种情况下,您可以使用命名空间别名吗?XBnamespace AB
0赞 Luchian Grigore 1/9/2013
@rhalbersma现有命名空间 - 仍然存在。A

答:

1赞 BЈовић 1/9/2013 #1

最好的方法是修复代码。

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

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

评论

0赞 Luchian Grigore 1/9/2013
是的,我也这么认为,这就是我所做的(不删除指令,这不是一个选项)。using
4赞 TemplateRex 1/9/2013 #2

我意识到这更多的是关于新代码,而不是重构现有代码,但我喜欢使用在这种情况下调用的特殊标头。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...)。

注1:AFAIK,使用Peter Wood的答案和你的声明之间没有技术上的区别。请注意,指令可能会导致麻烦,因为 Argument-Dependent-Lookup 会忽略这些指令。更糟糕的是,你的一些代码甚至可能以静默方式调用错误的函数重载,因为你不再拉取新的命名空间了!typedefusingusingusing namespace B;B

注2:在对问题的评论中,给出了一个Ideone的例子。这很好地说明了命名空间内名称查找的微妙之处:引用标准草案第 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 头文件中使用单个声明非常方便。

评论

0赞 Sebastian Redl 1/9/2013
让 struct 声明和 typedef 共享一个名称仍然是一个错误。typedef 也不会影响 ADL。
0赞 TemplateRex 1/10/2013
@SebastianRedl Tnx,更新以反映 using 指令和 typedef/using 声明之间的细微差别。
0赞 Luchian Grigore 1/10/2013
+1 用于最后一个微妙之处 - 我在自己解决问题时看到了这一点,但无法解释原因。
0赞 TemplateRex 1/11/2013
@LuchianGrigore我在寻找精确细节时玩得很开心。该标准在第 3.4 节中开始非常滑稽:“名称查找规则统一适用于所有名称 [...]”,然后是 14(!) 页的不合格、合格、使用指令、使用声明和其他复杂的细节;-)