C++ 我可以使用相同的名称为周围作用域中的类型声明成员类型别名吗?

C++ 我可以使用相同的名称为周围作用域中的类型声明成员类型别名吗?,c++,g++,c++14,clang++,type-alias,C++,G++,C++14,Clang++,Type Alias,出于元编程目的,我希望结构包含另一个类型的类型别名: struct Foo {}; struct WithNestedTypeAlias { using Foo = Foo; }; 然后我可以用NestedTypeAlias::Foo在模板中执行类似的,等等 据我所知,此类型别名是有效的,因为它不会更改Foo类型的含义。Clang很高兴地编写了这个 然而,GCC抱怨: test-shadow-alias.cpp:4:20: error: declaration of ‘using F

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

struct Foo {};

struct WithNestedTypeAlias {
    using Foo = Foo;
};
然后我可以用NestedTypeAlias::Foo在模板中执行类似的
,等等

据我所知,此类型别名是有效的,因为它不会更改
Foo
类型的含义。Clang很高兴地编写了这个

然而,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
的含义

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

注:

  • 使用clang++3.8和gcc 5.4进行测试,但是

  • 我看了一下,其中变量的名称可能引用外部范围中的变量或类成员。相反,我这里的问题是关于类型别名的。没有歧义,因为
    Foo
    总是指类范围内的
    ::Foo
    。我不明白那里的答案如何适用于我的问题

  • 这可能是因为误解了别名的实际类型


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

2) 类S中使用的名称N在其上下文中以及在完成的S范围内重新评估时应引用相同的声明。违反此规则无需诊断

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

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

struct N { };

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

  struct N { char buf[100]; };
};
在本例中,名称
N
改变了含义,对成员重新排序将改变
S::array
的大小。定义
S::array
N
指类型
::N
,但在
S
的完整范围内,它指的是
S::N
。这违反了上面引用的规则


在您的示例中,名称
Foo
的变化危险性要小得多,因为它仍然引用相同的类型,但严格来说,它确实从引用
:Foo
的声明变为
S::Foo
的声明。该规则的措辞是引用声明,因此我认为GCC是正确的。

使用Foo=:Foo将语句更改为
修复了它,但我无法解释原因也修复了它。请注意,等效的
typedef struct Foo Foo
是惯用的C代码。嗯,这个规则是有意义的。但这似乎意味着我不应该使用弗朗索瓦·安德烈在上面的评论中提到的
使用Foo=::Foo
技巧?虽然这一点目前正在发挥作用,但它仍然是
Foo
的新声明,因此违反了您引用的规则,对吗?因此,这将留给我唯一的解决方案
struct AnotherFoo{};嵌套结构{using Foo=AnotherFoo;};使用Foo=另一个Foo
用于可移植地执行嵌套的
Foo
声明。@amon:原始代码中的问题是,右侧的
Foo
更改了含义,因为它是不合格的。在使用
声明之前,它意味着全局Foo,之后它意味着别名。如果它是合格的,那么这就不再是一个问题。@amon还注意到,这将回答您关于
Foo
::Foo
和所有内容的问题。@amon规则不是说
Foo
不能更改其含义,而是在其含义更改之前不得使用它。因此
使用Foo=::Foo
是可以的,因为非限定的
Foo
在其含义更改之前不会被使用。类似地,如果我们简单地用
1
@Oktalist替换
sizeof(N)
,答案中的示例就可以了,我现在明白了。感谢您的帮助:)