为什么我不能使外部类型看起来像 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
来设置Inner
s。
我想在制作新的外部类型时尽可能保留名称:
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;
}
Inner
的 typedef
需要引用 外部范围 中的 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);
}
我想维护一个大量使用内部枚举的现有 API,但我需要将枚举分离成它自己的类型:
这是当前的API:
class Outer {
public:
enum Inner {
FIELD1
};
};
换句话说,用户目前希望通过使用Outer::Inner
来设置Inner
s。
我想在制作新的外部类型时尽可能保留名称:
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;
}
Inner
的 typedef
需要引用 外部范围 中的 Inner
枚举,因此它应该如下所示:
typedef ::Inner Inner;
虽然我通常更喜欢 using
语句,它看起来像这样:
using Inner = ::Inner;
这是一个demo
这是不允许的,因为规则 [basic.scope.class]/3:
A name
N
used in a classS
shall refer to the same declaration in its context and when re-evaluated in the completed scope ofS
. 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);
}