为什么我们不能在类内部声明命名空间别名?

15
在一个类内部似乎无法声明命名空间别名;然而,在函数级别上我们可以这样做(已在g++ 4.3.4上测试过):
namespace A
{
}

class C
{
  namespace N = A; // error: expected unqualified-id before `namespace'
};

class D
{
  void f();
};

void D::f()
{
  namespace N = A; // OK
}

有没有想法为什么存在这样的限制?这似乎与可以在类内声明的typedef不太一致。

4
+1,我不知道这是可能的:namespace N = A;意思是可以通过使用别名(alias)将命名空间 A 重命名为 N,使其更加方便地在代码中使用。 - Stephane Rolland
3
我理解您希望的是类似于这样的命名空间别名定义:A = A1::A2::A3::A4; - BЈовић
4
顺便说一下,这对我来说很新颖,加1个赞 :) - BЈовић
3个回答

6
根据C++标准3.3.6规定,以下规则描述了类中声明名称的作用域范围。
1)在类中声明的名称的潜在作用域不仅包括名称声明符后面的声明区域,还包括该类中的所有函数体、默认参数和构造函数初始化器(包括嵌套类中的这些内容)。
因此,在类作用域中只能声明上述列表中的内容。在类作用域中声明其他任何内容都是无效的,不仅限于命名空间联盟,还包括命名空间。例如:
class myClass
{
    //compilation error !!!
    namespace myNamespace
    {
    }
    using namespace std;//another compilation error
}

编辑:

为什么会存在这样的限制?这似乎与可以在类内部声明 typedef 的做法不太一致。

因为在类中使用 typedefs 非常有用(例如 vector<int>::iterator),而对于命名空间来说是无用的。考虑以下代码:

class myClass
{
   namespce N=std;
};

//now let's use N
MyClass::N::vector<int> v;//don't you think, that this syntax is horrible, and useless?????

为了比较,请看它在函数中的作用

void f()
{
    namespace bnu= boost::numeric::ublas;
    bnu::matrix<int> m;//and now we can use short name bnu
}

我们可以在cpp文件中声明命名空间alliance,而且不需要在类声明中声明。


不是针对个人攻击,但你错了:) 用你自己的 bnu 的例子,并注意现在可以在 myClass 方法的参数中使用 bnu,这非常有用,还可以在声明中返回值。此外,你在顶部的措辞“只有这个列表中的东西”没有意义,你指的是声明区域,它们是不能使用的。此外,你可以在类作用域中声明 typedef 和内部类,以及 using base::method 和 static_assert。禁止命名空间必须有另一个动机,超出了本页所提到的任何内容。 - v.oddou

5

我不是C++标准的专家,但我会冒险回答你的问题。我假设在类声明中使用namespace N = A违反了类成员定义的规定。

C++标准将类成员定义为

member-specification:
  member-declaration member-specification_opt
  access-specifier : member-specification_opt
member-declaration:
  decl-specifier-seq_opt member-declarator-list_opt ;
  function-definition ;opt
  ::opt nested-name-specifier templateopt unqualified-id ;
  using-declaration
  static_assert-declaration
  template-declaration
member-declarator-list:
  member-declarator
  member-declarator-list , member-declarator
member-declarator:
  declarator pure-specifier_opt
  declarator constant-initializer_opt
  identifier_opt : constant-expression
pure-specifier:
  = 0
constant-initializer:
  = constant-expression

重要的一点是在声明中的=,编译器期望的是一个纯指示符或常量初始化语句,因为该行没有以零结尾,所以在这种情况下我们不会应用纯指示符。

分析namespace N = A声明,编译器将其视为:

declarator = constant-expression

由于namespace是一个关键字,因此不能使用。

typedef是允许的,因为(根据标准)

嵌套类型是在类中定义的类和枚举, 以及通过使用typedef声明作为成员的任意类型。


1
这是信息性的,但并不具有教育意义。委员会为什么做出了这个决定?这就是我想知道的。我的打赌是因为他们预见到了由ADL引起的一系列问题。 - v.oddou

3

我不同意在类中声明命名空间是完全无用的。可以在类的命名空间内声明枚举,这将允许您使用特定数组的逻辑索引访问不同数组的元素。

class myClass
{
   private:
        namespace TransAndRotIdx {
            enum { U, V, W, P, Q, R }; };
        namespace RotIdx {
            enum { P, Q, R }; };
        double tr[6];
        double r[3];

    public:
        double getTranslationMag(void)
        {
            using namespace TransAndRotIdx;
            return sqrt(tr[U]*tr[U]+tr[V]*tr[V]+tr[W]*tr[W]);
        }
        double getRotationMag(void)
        {
            using namespace RotIdx;
            return sqrt(tr[P]*tr[P]+tr[Q]*tr[Q]+tr[R]*tr[R]);
        }
}

只是为了记录,在C++11中,你基本上可以使用“enum class”来实现这一点(但没有“using”声明的好处)。 - wjl

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接