Typedef и параметр шаблона с тем же именем

Почему этот случай неверен (он логичен)

template <typename T>
struct Der: public Base
{
    typedef int T;
    T val;
};

но этот случай правильный?

struct Base
{
    typedef int T;
};

template <typename T>
struct Der: public Base
{
    T val;
};

В стандарте 14.6.1/7 говорится:

В определении шаблона класса или в определении члена такого шаблона, который появляется за пределами определения шаблона, для каждого базового класса, который не зависит от параметра шаблона (14.6.2), если имя базового класса или имя элемента базового класса совпадает с именем параметра шаблона, имя базового класса или имя участника скрывает имя параметра шаблона (3.3.7).

Почему здесь нет двусмысленности?

Ответ 1

Первый пример неверен в соответствии с [temp.local]/6:

Параметр шаблона не должен быть обновлен в пределах его области (включая вложенные области).

Однако в

template <typename T>
struct Der: public Base
{
    T val;
};

T скрывается именем, унаследованным от Base - как указано вашей цитатой.

[..] , если имя базового класса или имя члена базовый класс совпадает с именем параметра шаблона, имя базового класса или имя участника скрывает имя параметра шаблона (3.3.7).

То есть член val имеет тип int. Демо.

Ответ 2

В целом, стандарт пытается убедиться, что значение типа в данный объем одинаковый.

Если мы притворимся, что typedef разрешено, рассмотрим типы val1, val2 и val3 в следующем?

template <typename T>
struct Der: public Base
{
    void f1() {
      T val1;      // What is the type of 'val1'?
    }

    T val2;        // What is the type of 'val2'?

    typedef int T;

    T val3;        // What is the type of 'val3'?
};

Единственная переменная, которая имеет тип параметра шаблона T, будет 'Значение2'.

Это потому, что для стандарта требуется, чтобы все члены класса были "в области" для f1. Вот почему функции-члены могут ссылаться на переменные-члены, определенные позже в классе.

Такую же проблему можно продемонстрировать и с помощью typedefs:

typedef int T;

struct S
{
  T x;
  typedef float T;
  T y;
};

Опять же, если это было законно, то T, используемый для объявления x, будет ссылаться на ::T, а T, используемый для y, будет ссылаться на typedef S::T.

Это распространяется на стандарт в соответствии с ISO 3.3.7/1:

Следующие правила описывают область имен, объявленных в классах.

...

2) Имя N, используемое в классе S, должно ссылаться на одно и то же объявление в его контексте и при повторной оценке в завершенной области S. Диагностика не требуется для нарушения этого правила.

Ответ 3

Потому что вторая однозначна:

Вам может не понравиться или знать, что в суперклассе есть typedev int T, но вы только что внесли T в качестве параметра шаблона, что позволяет понять, что вы ссылаетесь на него при использовании T в Der.