Я привык к обязательному использованию typename
в шаблонах классов, но не могу не задаться вопросом, действительно ли это необходимо. Этот вопрос задавали до здесь, на SO, но я был не вполне доволен ответами (и ни один из них не был, если я правильно понял).
Когда и где совершенно ясно (из cppreference.com):
В объявлении или определении шаблона, включая шаблон псевдонима, имя, которое не является членом текущего экземпляра и зависит от параметра шаблона, не считается типом, если не используется ключевое слово typename или если он уже был установлен как имя типа, например с объявлением typedef или путем использования базового класса.
Когда шаблон (класс или функция) задан (еще не создан), я понимаю, что утверждение
A::Type *tp;
где A
- это шаблонный параметр шаблона, а Type
должен быть вложенным типом, является неоднозначным. Type
может также быть статическим членом или значением перечисления или любыми другими возможностями, которые я не могу сейчас придумать. Оператором *
в этом случае является умножение (предполагая, что "умножение" является правильной формулировкой для реализации operator*()
), а не декларацией указателя. Однако я не могу думать о ситуации, когда такое утверждение может представлять собой умножение в классе. В области функции, конечно, но что это может означать в классе-сфере, отличном от объявления указателя?
Моя точка зрения заключается в том, что во многих случаях компилятор в какой-то момент ожидает имя типа, но требует от нас, чтобы мы это явно указывали, хотя других вариантов нет. Тот факт, что GCC и Clang испускают ошибку, а не предупреждение, являются довольно показательными:
template <typename T>
struct A
{
T::x *p;
};
GCC: error: need ‘typename’ before ‘T::x’ because ‘T’ is a dependent scope
CLANG: error: missing 'typename' prior to dependent type name 'T::x'
Другим убедительным случаем (по крайней мере для меня) является использование typedef
для зависимых от псевдонимов типов. В этом случае нет никаких сомнений в том, должен ли какой-либо тип ключевого слова typedef
быть типом (правильно?), Так почему бы устранить что-нибудь?
Подводя итог: во многих случаях, когда компилятор легко выводит из синтаксиса и контекста, означает ли программист тип или переменную, почему все же необходимо устранить неоднозначность?
ИЗМЕНИТЬ
Комментарии и предлагаемое дублирующее упоминание о том, что специализации могут усложнить ситуацию. Например (взято из ответа в другом потоке):
struct B {
typedef int result_type;
};
template<typename T>
struct C { }; // could be specialized!
template<typename T>
struct D : B, C<T> {
void f() {
// OK, member of current instantiation!
// A::result_type is not dependent: int
D::result_type r1;
// error, not a member of the current instantiation
D::questionable_type r2;
// OK for now - relying on C<T> to provide it
// But not a member of the current instantiation
typename D::questionable_type r3;
}
};
Я понимаю, что C
может быть специализированным для T
, и существует ли C<T>::questionable_type
, может быть разрешено только при создании экземпляра, но это не меняет того факта, что он должен быть типом. Синтаксис просто требует этого, поэтому почему disambiguate?