为什么我不能使外部类型看起来像 C++ 中的内部类型?

Why can't I make a external type appear like an inner type in c++?

我想维护一个大量使用内部枚举的现有 API,但我需要将枚举分离成它自己的类型:

这是当前的API:

class Outer {
public:
    enum Inner {
        FIELD1
    };
};

换句话说,用户目前希望通过使用Outer::Inner来设置Inners。 我想在制作新的外部类型时尽可能保留名称:

enum Inner {
    FIELD1
};

class Outer {
public:
    typedef Inner Inner;
}

但是 - 它给了我一个编译器错误:

./Playground/file0.cpp:23:19: error: declaration of 'typedef enum Inner Outer::Inner' changes meaning of 'Inner' [-fpermissive]
   23 |     typedef Inner Inner;
      |                   ^~~~~
./Playground/file0.cpp:17:6: note: 'Inner' declared here as 'enum Inner'
   17 | enum Inner {
      |      ^~~~~

如果我只是更改枚举的名称,一切正常。

#include <cassert>

enum Enum {
    FIELD1
};

class Outer {
public:
    typedef Enum Inner;
};

int main() {

    Enum field = Enum::FIELD1;
    Outer::Inner field_alt = Outer::Inner::FIELD1;
    assert(field == field_alt);
    return 0;
}

Innertypedef 需要引用 外部范围 中的 Inner 枚举,因此它应该如下所示:

typedef ::Inner Inner;

虽然我通常更喜欢 using 语句,它看起来像这样:

using Inner = ::Inner;

这是一个demo

这是不允许的,因为规则 [basic.scope.class]/3:

A name N used in a class S shall refer to the same declaration in its context and when re-evaluated in the completed scope of S. No diagnostic is required for a violation of this rule.

当您这样做 typedef Inner Inner; 时,您就违反了这条规则。 Inner 第一次出现在该行,它是名称的 use,并且被发现引用声明 ::Inner。但是一旦 class 被完全定义,Inner 现在指的是 typedef 声明。所以该程序是错误格式的 NDR(GCC 足够好,可以为您提供诊断)。

您需要将其更改为:

typedef ::Inner Inner;

现在,不合格的名称Inner不再被使用;相反,您使用的是限定名称 ::Inner,其含义在 typedef 之后保持不变,因此应该没有问题。

提供的解决方案回答了我原来的问题,但不幸的是没有解决我的问题。当时我还没有意识到 API 实际上利用了内部枚举。

enum Inner {
    FIELD1,
    FIELD2
};

class Base {
public:
    virtual void use(Inner val) = 0;
};

class Outer : public Base {
public:
    virtual void use(Inner val) override;
};

/*
 * Some API that can't change
 */
void someAPI(Outer &inst) {
    inst.use(Outer::FIELD1);
}

也就是说,我需要维护的API不是Outer::Inner::FIELD1而是Outer::FIELD1

正如@Brian 指出的那样,使用 typedef 'hoist' 将内部枚举转换为外部枚举在当前的 c++ 中无法工作。

最后发现可以把Inner放到Base里,API也能保持:

class Base {
public:
    enum Inner {
        FIELD1,
        FIELD2
    };

    virtual void use(Inner val) = 0;
};

class Outer : public Base {
public:
    virtual void use(Inner val) override;
};

/*
 * Some API that can't change
 */
void someAPI(Outer &inst) {
    inst.use(Outer::FIELD1);
}