Как утверждение T :: x (y) неоднозначно, если T является аргументом шаблона? - PullRequest
1 голос
/ 03 февраля 2012

Я читал информационный бюллетень по этому URL

http://publib.boulder.ibm.com/infocenter/comphelp/v8v101/index.jsp?topic=%2Fcom.ibm.xlcpp8a.doc%2Flanguage%2Fref%2Fkeyword_typename.htm

, и я понял, что понятия не имею, что может представлять тип T :: x.Вот выдержка

template<class T> class A
{
  T::x(y);
  typedef char C;
  A::C d;
}

Утверждение T :: x (y) неоднозначно.Это может быть вызов функции x () с нелокальным аргументом y или объявление переменной y с типом T :: x.C ++ будет интерпретировать это утверждение как вызов функции.Чтобы компилятор интерпретировал этот оператор как объявление, вы должны добавить ключевое слово typename в его начало.Утверждение A :: C d;плохо сформирован.Класс A также относится к A и, таким образом, зависит от параметра шаблона.Вы должны добавить ключевое слово typename в начало этого объявления:

Я хотел бы понять, как может существовать переменная y типа T :: x, как это будет работать и что это может быть возможноимею в виду?Каким будет х?

Спасибо: -)

Ответы [ 2 ]

6 голосов
/ 03 февраля 2012

В качестве разогрева к моему полному ответу рассмотрим следующее:

template <typename T> void IterateOverContainer(T container) {
    /* Error! */
    T::iterator itr(container.begin());
}

Здесь iterator - это тип, вложенный в T; например, std::vector<int>::iterator. Чтобы избежать неясностей, ключевое слово typename становится необходимым:

template <typename T> void IterateOverContainer(T container) {
    /* Now good! */
    typename T::iterator itr(container.begin());
}

Теперь это «ясно» имя типа (это то, что означает typename!), Поэтому ясно, что мы хотим объявить переменную, а не вызывать функцию.

Тем не менее, с новыми функциями C ++ 11 вы можете полностью обойти это с помощью auto:

template <typename T> void IterateOverContainer(T container) {
    auto itr(container.begin());
}

Или, если быть более понятным:

template <typename T> void IterateOverContainer(T container) {
    auto itr = container.begin();
}

Теперь, что касается вашего вопроса: как T::x(y) может когда-либо объявить переменную? Ну, из-за странной причуды C, это совершенно допустимое объявление переменной:

int (x);

Это так же, как

int x;

Итак, если бы у нас было что-то вроде этого:

template <typename T> void IterateOverContainer(T container) {
    /* Error! */
    T::iterator(itr);
}

Это можно интерпретировать как объявление переменной с именем itr типа T::iterator или как вызов функции T::iterator с передачей itr в качестве параметра. Использование typename устраняет неоднозначность, какой это.

Интересно, что это положение для дополнительных скобок является той же самой причиной, по которой существует Most Vexing Parse . Я надеюсь, что вы никогда не столкнетесь с этим. : -)

Надеюсь, это поможет!

0 голосов
/ 03 февраля 2012

Как уже говорилось, T::x может означать "тип x (возможно, class или typedef), который определен в T. Это также может означать" указатель на T " Функция члена s x.

Часто компилятор может выяснить, что является правильным на основе определенных сигналов (вы написали T::x foo или void (T::*)() foo = T::x?); Visual Studio делает, и GCC привык (они прекратили, потому что в то время люди получали свой код для компиляции в GCC, но не в Visual Studio). Стандарт требует typename для устранения неоднозначности сложных случаев.

...