c++命名空间头痛

tvel 发布于 2019-11-10 c++ 最后更新 2019-11-10 12:11 96 浏览

好的,这个问题已经发展了一些,我想尝试开始(过度)我正在拍摄的基本目标:

  • 在C++资源获取中创建包装旧C语言实体的库代码是初始化,并提供基本或更好的异常保证。
  • 使此代码的客户端能够以非常自然的C++方式使用它,从而为现有代码创建大量开销,将其转换为使用C++包装器对象(即自动转换为适当的遗留类型,构建器取代传统类型等。)
  • 限制库代码的名称空间影响。理想情况下,库将有几个子命名空间,提供相关功能,限制使用命名空间X类型声明的数量和影响 - 就像boost库一样(即使用细节命名空间只注入用户合理想要的那些符号)使用和隐藏那些是实现细节;还限制现有符号可能的新含义范围,以避免在用户代码中出现意外的隐式转换)
  • 要求客户明确要求库中那些他们实际想要注入其代码库的部分。这与限制包含图书馆标题的影响密切相关。客户端代码应该有一个合理的控制级别,可以在编译代码时自动使用库的哪些部分进行名称解析。
  • 我自己的库代码不应该充斥着重构 - 脆弱的代码构造。如果库的头文件不必经常声明私有typedef以便访问库的该部分的其余部分,那将是理想的。或者换句话说:我希望我的库能够像我的客户在使用所述库时那样直观地编写。除了明确“使用”的任何其他名称之外,名称解析还应包括定义库的名称空间。
我经常遇到这种情况,我正在寻找更好的方法...... 我有一个类,C在命名空间N.C有一个成员,Free。它免费是C管理的东西,并允许C管理新事物。 有几个全局免费功能。在与C相同的命名空间N中也有一些辅助函数,其中一个是一个帮助程序,它释放由C管理的东西,名为free。 所以我们有类似的东西:
namespace N {

void free(THING * thing);

class C { public: ... details omitted... free() { free(m_thing); // <- how best to refer to N::free(THING&) } }

} // namespace N

我可以使用N :: free(m_thing)。但这对我来说似乎很不幸。有没有办法引用类范围之外的但没有解析绝对命名空间(相对一步取出范围)? 在我看来,必须命名N :: free是令人讨厌的,因为如果这是一个独立的功能你就不必这么做了。如果班级的方法名称恰好不同(例如处置),你也不需要。但是因为我使用了相同的名字,所以如果你不想指明绝对路径 - 而不是相对路径 - 我将无法访问它,如果你放纵我的类比。 我讨厌绝对的道路。它们使命名空间中的移动变得非常脆弱,因此代码重构变得更加丑陋。此外,如何在函数体中命名事物的规则变得更加复杂,使用当前的规则集(正如我所理解的那样) - 不那么规律 - 在人们所期望的和作为程序员的人之间产生分裂。 有没有更好的方法来访问与类相同的命名空间中的独立函数,而不必绝对命名自由函数? 编辑: 也许我应该用一个不那么抽象的例子:

namespace Toolbox {
  namespace Windows {

// deallocates the given PIDL void Free(ITEMIDLIST ** ppidl);

class Pidl { public: // create empty Pidl() : m_pidl(NULL) { }

// create a copy of a given PIDL
explicit Pidl(const ITEMIDLIST * pidl);

// create a PIDL from an IShellFolder
explicit Pidl(IShellFolder * folder);

...

// dispose of the underlying ITEMIDLIST* so we can be free to manage another...
void Free();

};

所以ITEMIDLIST *来自各个地方,并使用CoTaskMemFree()销毁。我可以将Pidl作为全局名称引入 - 以及作为我的工具箱库一部分的“Windows Shell.h”头中的所有辅助函数。 理想情况下,我会根据它们的相关内容对我的库中的一些工具进行细分 - 在这种情况下,上述所有工具都与Windows中的COM编程有关。我已选择Toolbox作为我的库的基本命名空间,并且目前认为我将使用Toolbox :: Windows用于非常windows-y的函数,类等。 但是C++命名空间和名称解析规则似乎使这非常困难(因此这个问题)。创建我的代码的这种分段是非常不自然的 - 因为koenig查找失败(因为ITEMIDLIST不在我的Toolbox :: Windows命名空间中),而且我没有能力将它移动到那里!我也不应该。语言应该足够灵活,IMO,允许扩展库(如我的Toolbox库)扩展其他人的代码,而不必将我的扩展注入其命名空间(在Win32和一般广大的情况下)今天存在的大多数代码都是GLOBAL NS--这首先是制作名称空间的全部要点:避免全局NS拥挤/污染/模糊/编程意外)。 所以,我回过头来看,有没有更好的方法:扩展现有的代码库,同时不用我的扩展来污染他们的NS,但仍然允许直观和有用的名称解析,如果我的代码在他们的NS中但是我的代码客户明确地介绍了(即我不想肆无忌惮地注入我的代码,但只是在明确要求时)? 另一个想法:如果我有以下内容,也许满足我上面的criterea的是:

using namespace X {
  code here...
}
我可以在任何地方放置这样的构造,包括在标题中,我不必担心将X拖入我的客户端的代码中,但是如果我在的话,我可以自由地编写源代码。根命名空间。
已邀请:

qporro

赞同来自:

你有两个选择:

  1. 完全限定功能名称。
  2. Koenig lookup完成工作(如果THING属于名称空间N)。
我会选择第一个具有如free这样流行的函数名称。 替代选项是使用m_thing->Release()或类似的东西。

iut

赞同来自:

Yuu可以写::free(m_thing);。这将调用全局free()函数。但是,如果你在N命名空间之外有另一个free()函数,那么你就遇到了麻烦。要么更改某些函数的名称,要么使用带有显式名称空间的函数名称。

aquia

赞同来自:

我将使用一种完全不同的方法来实现您想要完成的任务。我想你需要重新设计一下。删除全局变量并创建一个工厂/抽象工厂,它将构造(创建类的实例)并使用析构函数将其销毁。事实上,这将是RAII的成语。

lipsum

赞同来自:

在我看来,这里存在设计问题。 我觉得奇怪的是将相同的标识符用作自由函数和类方法,它确实令人困惑。尽管我承认它适用于swap,但我尽量尽量减少出现的次数...... 我尽量不对方法中的名称进行限定,但是当然你冒着hiding的风险,其中MyClass::free隐藏了N::free方法,该方法从不参与查找...试图在这里查找范围分辨率但找不到确切的段落。 对于交换,我只需使用:

class MyClass
{
  void swap(MyClass& rhs)
  {
    using std::swap;         // Because int, etc... are not in std
    swap(m_foo, rhs.m_foo);
    swap(m_bar, rhs.m_bar);
  }
};
inline void swap(MyClass& lhs, MyClass& rhs) { lhs.swap(rhs); }
因此,我确保正确的方法将包含在查找中,并避免回退到“全局”方法(如果有的话)。 我更喜欢这种方法来显式命名,并且通常在方法的顶部固定usingtypedef声明,以便实际代码不会膨胀。

dipsam

赞同来自:

我没有看到避免在这里限定命名空间范围free(),但应该注意这与“绝对路径”不同。首先,如果你有嵌套的命名空间,你只需要引用最里面的命名空间:

namespace N1 {
  namespace N2 {
    namespace N3 {
      void free(THING * thing);
class C {
      public:
        free() {
          N3::free(m_Thing); // no need to do N1::N2::
        }
      };
    }
  }
}
[编辑]回应编辑过的问题。同样,我没有看到任何方法在您描述的确切场景中执行此操作。但是,它似乎不是惯用的C++方法 - 更常见的方法是为ITEMIDLIST提供自己的包装类,管理所有分配,RAII样式,并公开原始句柄(例如通过转换操作符a如果您想要额外的安全性,请使用AT ATL或显式成员函数,如c_str()。这将完全取消对free的需求,并且对于您可能需要的任何其他免费函数,由于您控制了包装类型和它所在的命名空间,您可以像往常一样使用ADL。 [编辑#2]。这部分问题:
allow for intuitive and useful name resolution as one would expect if my code were in their NS but explicitly introduced by the client of my code (i.e. I don't want to inject my code willy-nilly, but only upon explicit request)?
这不正是你把它放在命名空间中,而你的客户端写using namespace ...会实现吗?