我可以使用相同的名称为周围范围内的类型声明成员类型别名吗?

2022-01-23 00:00:00 g++ c++ c++14 clang++ type-alias

出于元编程目的,我希望结构包含另一个类型的类型别名:

I want a struct to contain a type alias to another type for metaprogramming purposes:

struct Foo {};

struct WithNestedTypeAlias {
    using Foo = Foo;
};

然后我可以在模板等中执行 WithNestedTypeAlias::Foo 之类的操作.

Then I can do stuff like WithNestedTypeAlias::Foo in a template etc.

据我了解,这种类型别名是有效的,因为它不会改变 Foo 类型的含义.Clang 愉快地编译了这个.

As I understand, this type alias is valid because it does not change the meaning of the Foo type. Clang compiles this happily.

但是,GCC 抱怨:

test-shadow-alias.cpp:4:20: error: declaration of ‘using Foo = struct Foo’ [-fpermissive]
     using Foo = Foo;
                    ^
test-shadow-alias.cpp:1:8: error: changes meaning of ‘Foo’ from ‘struct Foo’ [-fpermissive]
 struct Foo {};
        ^

现在我很困惑,因为我明确没有从 struct Foo 更改 Foo 的含义.

Now I'm confused because I'm explicitly not changing the meaning of Foo from struct Foo.

C++14 的正确行为是什么?我知道我可以通过重命名 struct Foo 来解决这个问题,但我想了解 GCC 的错误在这里是否正确.

What is the correct behaviour for C++14? I know I can work around this by renaming the struct Foo, but I'd like to understand whether GCC's error is correct here.

注意事项:

  • 使用 clang++ 3.8 和 gcc 5.4 测试,但 Godbolt 建议这在最近的 GCC 中没有改变版本.

我查看了 decltype 与隐藏外部名称的类成员名称之间的交互,其中变量 可以引用外部范围内的变量或类成员.相反,我的问题是关于类型别名.没有歧义,因为 Foo 总是在类范围内引用 ::Foo.我看不出那里的答案如何适用于我的问题.

I looked at Interaction between decltype and class member name shadowing an external name, where the name of a variable may refer to either a variable in the outer scope or to a class member. In contrast, my question here is about a type alias. There is no ambiguity since Foo always refers to ::Foo within the class scope. I don't see how the answer there applies to my problem.

这可能是因为对别名的真正含义有误解.

This is probably due to a misunderstanding of what type aliases actually are.

推荐答案

GCC 正在执行的规则在 [basic.scope.class] 中:

The rule GCC is enforcing is in [basic.scope.class]:

2) 在类 S 中使用的名称 N 应在其上下文中引用相同的声明,并且在 S 的完整范围内重新评估时.违反此规则不需要诊断.

2) 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.

标准规定违反此规定不需要诊断,因此 GCC 和 Clang 可能都符合要求,因为(如果 GCC 正确)代码无效,但不需要编译器进行诊断.

The standard says violating this doesn't require a diagnostic, so it's possible that both GCC and Clang are conforming, because (if GCC is right) the code is not valid, but the compiler is not required to diagnose it.

此规则的目的是使类中使用的名称始终表示相同的含义,并且重新排序成员不会改变它们的解释方式,例如

The purpose of this rule is so that names used in a class always mean the same thing, and re-ordering members doesn't alter how they are interpreted e.g.

struct N { };

struct S {
  int array[sizeof(N)];

  struct N { char buf[100]; };
};

在此示例中,名称 N 改变了含义,重新排序成员将改变 S::array 的大小.当定义 S::arrayN 指的是类型 ::N 但在 S 的完整范围内它指的是 S::N .这违反了上面引用的规则.

In this example the name N changes meaning, and reordering the members would change the size of S::array. When S::array is defined N refers to the type ::N but in the completed scope of S it refers to S::N instead. This violates the rule quoted above.

在您的示例中,名称 Foo 的更改危险性要小得多,因为它仍然引用相同的类型,但严格来说,它确实与引用 :: 的声明有所不同FooS::Foo 的声明.该规则是根据引用声明来表述的,所以我认为 GCC 是正确的.

In your example the name Foo changes in a far less dangerous way, because it still refers to the same type, however strictly speaking it does change from referring to the declaration of ::Foo to the declaration of S::Foo. The rule is phrased in terms of referring to declarations, so I think GCC is right.

相关文章