Ключевые слова "typename" и "template": действительно ли они нужны?

На этом сайте есть много вопросов с проблемами при компиляции кода шаблона С++. Одним из наиболее распространенных решений таких проблем является добавление ключевого слова typename (и, реже, template) в нужные места кода программы:

template<typename T>
class Base
{
public:

    typedef char SomeType;

    template<typename U>
    void SomeMethod(SomeType& v)
    {
        // ...
    }

};

template<typename T>
class Derived : public Base<T>
{
public:

    void Method()
    {
        typename Base<T>::SomeType x;
    //  ^^^^^^^^

        this->template SomeMethod<int>(x);
    //        ^^^^^^^^
    }
};

Есть ли код, который компилируется как с ключевым словом typename, так и без него и дает разные результаты (например, выходные строки)? Аналогичный вопрос для ключевого слова template.

Если нет, нужны ли эти ключевые слова в таких значениях?

Небольшой обзор текущей ситуации

@Paul Evans написал хороший ответ, но он более подходит для вопроса "Где и почему мне нужно поставить ключевые слова" template "и" typename "?" , а не на мой вопрос.

@Simple привел пример требуемого кода для typename ключевое слово и его возможные варианты. @Jarod42 дал другую вариацию без каких-либо шаблонов, Вероятно, это ошибка gcc, потому что не компилируется с clang.

@nm привел пример требуемого кода для template ключевое слово, @dyp улучшено. @nm также написал другой код для обоих ключевых слов используя SFINAE.

@James Kanze в своем ответе утверждает, что невозможно записать требуемый код, и любые попытки сделать это приведут к undefined. Таким образом, приведенные выше примеры кода являются незаконными.

Интересно узнать, кто прав и что говорит об этом в стандарте С++.

Ответ 1

Правило: typename должно использоваться, когда имя, зависящее от параметра шаблона, является типом. Есть четкие случаи, когда это необходимо, считают

template <typename T> 
class Foo { 
    typename T::type * p; 
    //... 
}; 

Здесь второй typename используется, чтобы указать, что type - это тип, определенный внутри class T. Таким образом, p является указателем на тип T::type.

Без typename, type будет считаться членом class T. Таким образом, выражение:

T::type * p

будет умножением члена type class T с p.

Аналогично, правило: .template, ->template или ::template должно использоваться при доступе к члену шаблона, который использует параметр шаблона. Рассмотрим:

 p->template SomeMethod<int>(x);

без использования template, компилятор не знает, что токен < не меньше, чем начало списка аргументов шаблона.

Ответ 2

По определению невозможно написать код, который отличается в значение с ключевым словом или без него; любая попытка сделать это будет приводят к поведению undefined. Единственная цель этих ключевые слова (по крайней мере, в этом контексте) позволяют компилятору полностью проанализировать шаблон до создания экземпляра: для того, чтобы правильно разобрать С++, компилятор должен знать, есть ли символ обозначает тип, шаблон или что-то еще.

Как только вы создаете экземпляр шаблона, компилятор больше не работает нужны ключевые слова; они не влияют на поиск имени, или что-нибудь еще. Единственная особенность заключается в том, что если имя найдено ставит ложь в вашу декларацию (вы сказали тип, и в экземпляр, это не тип), происходит поведение undefined. Если, например, компилятор сохранил шаблон в форме дерево разбора, это дерево разбора будет неверным, и кто знает что это может означать.

Я бы ожидал, что большинство хороших компиляторов отметят категорию в первом анализе, и если поиск имени возвращает что-то еще, испускать ошибку, но по историческим причинам я подозреваю, что там все еще являются компиляторами, которые более или менее игнорируют template или typename в этом контексте, рассматривая его как комментарий; хранить шаблон как последовательность токенов и только разбор шаблон создается, используя категории, которые он фактически находит в экземпляре.

EDIT:

Я перечитывал части стандарта, и я больше не убедитесь, что существует поведение undefined. С++ 11, по крайней мере, говорит:

Имя, используемое в объявлении или определении шаблона, и которое в зависимости от параметра шаблона, предполагается, что не указывать тип если подходящий поиск имени не найдет имя типа или имя является квалифицированным "по ключевому слову typename.

Когда квалифицированный идентификатор предназначен для обозначения типа, который не является член текущего экземпляра и его Вложенное имя-спецификатор относится к зависимому типу, оно должно быть с префиксом ключевого слова typename, формирование typename-specifier. Если идентификатор тип-спецификатор не обозначает тип, программа плохо сформирован.

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