66

在类中声明类是有效的。(嵌套类)

在类中声明命名空间是无效的。

问题是:有什么好的理由(除了 c++ 语法/语法问题)禁止在类中声明命名空间吗?


至于我为什么要这样做,这里有一个例子:

让我们有一个二叉树容器的基本说明

template<typename Data>
class binary_tree
{
 public:
  ... stuff ....     

 private:
  ... iterators class declaration ...

 public:
  typedef left_depth_iterator_impl     left_depth_iterator;
  typedef right_depth_iterator_impl    right_depth_iterator;
  typedef left_breadth_iterator_impl   left_breadth_iterator;
  typedef right_breadth_iterator_impl  right_breadth_iterator;

  ... stuff ....     

 private:
  Data         data;
  binary_tree* left;
  binary_tree* right;
};

现在我注意到我的类中有很多迭代器,所以我想在同一个命名空间中重新组合它们,如下所示:

template<typename Data>
class binary_tree
{
 public:
  ... stuff ....     

 private:
  ... iterators class declaration ...

 public:
  namespace iterator
  {
    typedef left_depth_iterator_impl     left_depth;
    typedef right_depth_iterator_impl    right_depth;
    typedef left_breadth_iterator_impl   left_breadth;
    typedef right_breadth_iterator_impl  right_breadth;
  }

  ... stuff ....     

 private:
  Data         data;
  binary_tree* left;
  binary_tree* right;
};

这将允许一个简单的用法:

void  function()
{
  binary_tree::iterator::left_depth   it;

  ...stuff...
}

如果我使用一个类而不是命名空间,这会起作用,但是我被迫声明一个永远不会被实例化的类,它是一个相当命名空间。

为什么允许嵌套类并禁止在类中嵌套命名空间?这是遗留负担吗?


具有语义原因的答案不仅引用标准的一部分(尤其是语法部分)将被欣赏:)

4

6 回答 6

58

既然您询问了标准授权命名空间位置的哪些部分,我们首先提出了这个问题:

C++11 7.3-p4:每个命名空间定义应出现在全局范围或命名空间范围 (3.3.6) 中。

关于类定义和在其中声明命名空间的提议,我带你去......

C++11 9.2-p2:类在类说明符的结束 } 处被视为完全定义的对象类型(3.9)(或完整类型)。在类成员规范中,类在函数体、默认参数、异常规范和非静态数据成员(包括嵌套类中的此类内容)的大括号或等式初始化器中被认为是完整的。否则,它在其自己的类成员规范内被视为不完整。

因此,一旦达到结束卷曲,类定义是有限的。它不能被打开备份和扩展(派生是不同的,但它不是扩展刚刚定义的类)。

但是潜伏在命名空间标准定义的最开始的是扩展它的能力。由于缺乏更好的术语而对其进行扩展:

C++ 7.3-p1:命名空间是一个可选命名的声明区域。命名空间的名称可用于访问在该命名空间中声明的实体;即命名空间的成员。与其他声明性区域不同,命名空间的定义可以拆分为一个或多个翻译单元的几个部分。(强调补充)。

因此,类中的命名空间将违反 7.3-p4 中的定义。假设不存在,则可以在任何地方声明命名空间,包括在类中,但由于类的定义一旦关闭就正式化,如果您维护,您将只能执行以下操作符合 7.3-p1:

class Foo
{
   namespace bar
   {
       ..stuff..
   }

   .. more stuff ..

   namespace bar
   {
       ..still more stuff..
   }
};

在建立 7.3-p4 来解决它之前,这个功能的实用性可能已经争论了大约 3 秒。

于 2012-11-21T00:35:16.157 回答
28

我在这里不同意其他人的意见。我不会说没有真正的优势。有时我只想在没有额外含义的情况下隔离代码。例如,我在一个多线程 ringbuffer 模块中工作,并希望将状态成员(其中一些是原子的和/或内存对齐的)拆分为生产者和消费者的命名空间。

通过仅使用producerconsumer前缀命名所有内容(这是我当前令人讨厌的实现),我正在添加污染,使代码更难阅读。例如,当生产者拥有的所有内容都以 开头时producer,您的大脑在读取它时会更容易意外地自动更正producerProducerTimer(生产者计时器的生产者副本)为producerConsumerTimer(消费者计时器的生产者影子)或consumerProducerTimer(生产者计时器的消费者影子)。由于代码不再可浏览,因此调试所需的时间比需要的时间长。

通过创建嵌套类/结构:

  • 我可能会给下一个维护此代码的开发人员一个想法,即在上下文中可以/应该实例化、复制和分配多个这些代码中的一个以上,所以现在不仅担心命名,我还必须处理= delete这些事情.
  • 我可以使用结构对齐填充将内存占用添加到上下文中,否则这可能是不必要的。
  • 将所有成员设为静态不是一种选择,因为可以实例化多个需要其自己的生产者/消费者状态变量的上下文。
  • 这种结构的函数不再可以访问其他成员数据或函数,例如双方共享的常量或函数,而是必须将这些东西作为参数。

理想情况下,我希望能够改变这样的事情:

rbptr producerPosition;
rbptr consumerPosition;

对此:

namespace producer
{
    rbptr position;
}
namespace consumer
{
    rbptr position;
}

然后,应该只接触消费者成员的函数可以使用消费者命名空间,只应该接触生产者成员的函数可以使用生产者命名空间,而需要接触两者的函数必须明确限定它们。在仅使用生产者命名空间的函数中,无法意外触及消费者变量。

在这种情况下,希望纯粹是为了减少事物的生产者和消费者副本之间的命名冲突,而减少命名冲突是命名空间存在的目的。出于这个原因,我支持能够在类中声明命名空间的提议。

于 2017-07-13T17:38:47.497 回答
9

将这样的功能添加到语言中并没有真正的优势。除非有需求,否则通常不会添加功能。

类中的命名空间会给你带来什么?你真的宁愿说binary_tree::iterator::left_depth而不是简单地说binary_tree::left_depth吗?也许如果你有多个命名空间,你可以用它们来区分 saybinary_tree::depth_iterator::leftbinary_tree::breadth_iterator::right.

在任何情况下,您都可以使用内部类作为差劲程序员的命名空间来获得所需的结果,这也是为什么类内部不需要真正的命名空间的更多原因。

于 2012-11-21T00:20:10.773 回答
4

这不是命名空间的重点。命名空间旨在更接近代码的顶层存在,以便两个不同的公司(或代码库)可以相互混合代码。在更微观的层面上,我使用用于电子邮件访问的 IMAP 和用于发送电子邮件的 SMTP 进行编码,并且(可以,我正在大大简化)在任何一个模块Email中都有完全不同的类,但我可以有一个应用程序,比如邮件客户端,希望同时使用同一类中的两者,例如,它可能会将邮件从一个帐户转发到另一个帐户。命名空间/包名称/等允许这样做。

您所提出的根本不是名称空间的用途-在一个文件中,您可以给事物起不同的名称,因为作者对该文件具有全局知识,尽管当两家公司想要共享代码或两个应用程序时,情况并非如此那不知道他们会在任何时候发生碰撞。

于 2012-11-21T00:15:24.727 回答
3

只是一个我觉得值得一提的小想法。在类中使用命名空间的一种方式是在功能上等同于模板命名空间。

template<class...types>
struct Namespace {
    namespace Implementation {
        ...
    }
};

// somewhere else

using namespace Namespace<types...>::Implementation;

// use templated stuff.

就个人而言,我会喜欢这个功能,但似乎需求还不够高,无法实现。

于 2018-08-16T19:11:39.133 回答
0

我可能会为此讨厌

template<typename Data>
class binary_tree
{
 public:
  ... stuff ....     

 private:
  ... iterators class declaration ...

 public:
  class iterator
  {
    iterator() = delete;
  public:
    typedef left_depth_iterator_impl     left_depth;
    typedef right_depth_iterator_impl    right_depth;
    typedef left_breadth_iterator_impl   left_breadth;
    typedef right_breadth_iterator_impl  right_breadth;
  }

  ... stuff ....     

 private:
  Data         data;
  binary_tree* left;
  binary_tree* right;
};

这基本上是一个无法实例化的静态类,因此您必须使用它访问其中的 typedef,iterator::type 以便您现在可以将其用作常规命名空间

void  function()
{
  binary_tree::iterator::left_depth   it;

  ...stuff...
}
于 2022-03-05T19:31:49.957 回答