Я хочу, чтобы структура содержала псевдоним типа для другого типа в целях метапрограммирования:
struct Foo {};
struct WithNestedTypeAlias {
using Foo = Foo;
};
Тогда я могу делать такие вещи, как WithNestedTypeAlias::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 {};
^
Теперь я в замешательстве, потому что я явно не меняю значение Foo
от struct Foo
,
Каково правильное поведение для C ++ 14? Я знаю, что могу обойти это, переименовав struct Foo
, но я хотел бы понять, является ли ошибка GCC здесь правильной.
Заметки:
Протестировано с clang ++ 3.8 и gcc 5.4, но Годболт предполагает, что это не изменилось в более поздних версиях GCC.
я смотрел на Взаимодействие между decltype и именем члена класса, скрывающим внешнее имя, где имя переменная может ссылаться либо на переменную во внешней области видимости, либо на члена класса. Напротив, мой вопрос здесь о псевдониме типа. Там нет двусмысленности, так как Foo
всегда относится к ::Foo
в рамках класса. Я не понимаю, как ответ там относится к моей проблеме.
Вероятно, это связано с неправильным пониманием того, что на самом деле являются псевдонимами типов.
Правило, которое GCC применяет, находится в [basic.scope.class]:
2) Имя N, используемое в классе S, должно относиться к тому же объявлению в его контексте и при повторной оценке в завершенной области 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 прав.
Других решений пока нет …