Проверить ошибку в GCC

Я хотел бы проверить, что следующее является ошибкой в ​​GCC, а не в моем понимании С++. Рассмотрим следующий код:

struct A
{
    struct B
    {
        template< typename U > U as() const { return U(); }
    };

    B operator[]( int ) const { return B(); }
};

template< typename T >
struct as
{
    template< typename U >
    static T call( const U& u )
    {
        return u[ 0 ].as< T >(); // accepted by Clang 3.2, rejected by GCC 4.7
        // return u[ 0 ].template as< T >(); // does not help and is IMHO not needed
        // return u[ 0 ].A::B::as< T >(); // accepted by GCC 4.7
    }
};

int main()
{
    as< int >::call( A() );
}

IMHO код должен быть точным, он принят Clang 3.2, но не GCC 4.7 (4.4 и 4.6 тоже не сбой с той же самой ошибкой, но 4.4 дает немного другое сообщение). Здесь вывод из моей оболочки:

$ clang++-3.2 -O3 -Wall -Wextra -std=c++0x t.cc -o t
$ g++-4.7 -O3 -Wall -Wextra -std=c++0x t.cc -o t
t.cc: In static member function ‘static T as<T>::call(const U&)’:
t.cc:17:21: error: invalid use of ‘struct as<T>’
t.cc: In static member function ‘static T as<T>::call(const U&) [with U = A; T = int]’:
t.cc:18:4: warning: control reaches end of non-void function [-Wreturn-type]
$ 

Вопрос: Является ли это ошибкой в ​​GCC или мне что-то не хватает?

EDIT: Я немного смущен: отчет об ошибке GCC в http://gcc.gnu.org/bugzilla/show_bug.cgi?id=55576 говорит в комментарии # 9, что код в комментарии # 3 "действителен". Что именно это значит? Похоже, что люди GCC считают, что на самом деле это ошибка, иначе они уже закрыли бы ее? OTOH ответ от @Potatoswatter кажется совершенно ясным, что он должен быть правильным, и я должен подать отчет об ошибке против Clang (или там уже есть такой отчет об ошибке?)

Обратите внимание, что я смущаюсь отмечать ответ как принятый до тех пор, пока не будет разъяснено выше. Поскольку оба ответа уже полезны (один для объяснения, один для обхода), я дал оба значения.

Бонусный вопрос: так как я получил нисходящий рейтинг для ненавязчивого кода, мне интересно, как это себя чувствуют другие. Я попытался создать SCCEE, который удаляет все отвлечения и концентрирует внимание на технической проблеме. Это то, что я предпочитаю думать об этих вещах. Это неправильно?

Кроме того, @EdHeal: Почему код подвержен катастрофе? (Вы не думаете, что это реальный код, который у меня есть?)

EDIT2: Спасибо, Дэвид, просто заметил ваше редактирование. Я помечаю ваш ответ как принятый сейчас, и я также заметил, что вы прокомментировали сообщение об ошибке GCC. Я думаю, что главный вопрос этого вопроса ответил на это, и GCC получил еще одно напоминание. Спасибо всем.

Ответ 1

Это сложный угол языка. GCC применяет правило из С++ 03 §3.4.5/1:

В выражении доступа к члену класса (5.2.5), если токен . или -> сразу за ним следует идентификатор, за которым следует <, идентификатор должен быть просмотрен, чтобы определить, будет ли < - это начало списка аргументов шаблона (14.2) или оператора меньше. Идентификатор сначала просматривается в классе выражения объекта. Если идентификатор не найден, он затем просматривается в контексте всего постфиксного выражения и называет шаблон класса или функции. Если поиск в классе выражения объекта находит шаблон, имя также просматривается в контексте всего постфиксного выражения и

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

- если имя найдено в контексте всего постфиксного выражения и не называет шаблон класса, используется имя, найденное в классе выражения объекта, в противном случае

- если найденное имя является шаблоном класса, оно должно ссылаться на тот же объект, что и тот, который найден в классе выражения объекта, в противном случае программа плохо сформирована.

Обратите внимание, что этот процесс был бесполезен, потому что ключевое слово template уже необходимо для устранения симпликации токена <, так как тип подвыражения u[0] зависит от аргумента шаблона.

Причиной для этого является упрощение синтаксического анализа в случае, когда идентификатор шаблона используется в квалификаторе вложенного имени, например u[ 0 ].as< T >::bar.baz, где bar является typedef для базового класса.

С++ 11 удаляет три пулевые точки, упрощает процесс до

Идентификатор сначала просматривается в классе выражения объекта. Если идентификатор не найден, он затем просматривается в контексте всего постфиксного выражения и называет шаблон класса.

Итак, это ошибка, а не старая, как я говорил ранее. Необходимо удалить поле поиска угла поиска.

Кроме того, похоже, что этот причуда можно использовать, чтобы одно шаблонное выражение могло попеременно ссылаться на класс или функцию. Не уверен, что это полезно, но оно новое в С++ 11.

Ответ 2

Проблема заключается в том, что as вводится в область template<...> struct as как имя класса и шаблона; поэтому gcc жалуется на "invalid use of ‘struct as<T>’".

Я не совсем уверен, правилен ли gcc (это относится к правилам поиска имен в выражениях членов), но обходным путем является использование decltype:

    return u[ 0 ].decltype(u[ 0 ])::template as< T >();