Typedef изменяет значение
Когда я компилирую следующий фрагмент с g++
template<class T>
class A
{};
template<class T>
class B
{
public:
typedef A<T> A;
};
компилятор говорит мне
error: declaration of ‘typedef class A<T> B<T>::A’
error: changes meaning of ‘A’ from ‘class A<T>’
С другой стороны, если я изменю typedef
на
typedef ::A<T> A;
все компилируется с помощью g++
. Clang++ 3.1 все равно.
Почему это происходит? И второй стандарт поведения?
Ответы
Ответ 1
g++ является правильным и соответствует стандарту. Из [3.3.7/1]:
Имя N, используемое в классе S, должно ссылаться на одно и то же объявление в его контекста и при повторной оценке в завершенном объеме С. Нет для нарушения этого правила требуется диагностика.
До typedef A
ссылается на ::A
, однако, используя typedef, вы теперь делаете A
ссылкой на typedef, который запрещен. Однако, поскольку no diagnostic is required
, clang также стандартно соответствует.
комментарий jogojapan объясняет причину этого правила.
Внесите в код следующие изменения:
template<class T>
class A
{};
template<class T>
class B
{
public:
A a; // <-- What "A" is this referring to?
typedef A<T> A;
};
Из-за того, как работает область видимости класса, A a;
становится неоднозначным.
Ответ 2
Я добавлю к ответу Джесси о кажущемся своеобразным поведением GCC при компиляции:
typedef A<T> A;
против
typedef ::A<T> A;
Это также относится к использованию утверждений также формы:
using A = A<T>;
using A = ::A<T>;
То, что, кажется, происходит в GCC, заключается в том, что во время компиляции оператора typedef/using, объявляющего B:: A, символ B:: A становится допустимым кандидатом внутри самого оператора using. То есть при высказывании using A = A<T>;
или typedef A<T> A;
GCC рассматривает как ::A
, так и B::A
допустимых кандидатов для A<T>
.
Это кажется странным поведением, потому что, как следует из вашего вопроса, вы не ожидаете, что новый псевдоним A станет действительным кандидатом в самом typedef, но, как говорит Джесси, все, что объявлено внутри класса, становится видимым для всего остального внутри класс - и в этом случае, по-видимому, даже сама декларация. Этот тип поведения может быть реализован таким образом, чтобы разрешить рекурсивные определения типов.
Решение, которое вы нашли, это указать для GCC именно то, к которому вы обращаетесь в пределах typedef, а затем оно больше не жалуется.