Недопустимый шаблон зависимой функции-члена шаблона вычета-думает, что я пытаюсь использовать std:: set

у меня есть шаблон класса, который наследует от шаблона базового класса.

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

я знаю, что для устранения неоднозначности вызова шаблона функции-члена я должен использовать template ключевое слово, и я должен явно ссылаться на this в супер класс.

this->base_member_obj.template member_function<int>();

все это хорошо и хорошо, за исключением того, что кодовая база, которую я использую, сделала довольно неудачную ошибку импорта всего namespace std, и функция-член шаблона, которую я пытаюсь вызвать, называется set. Где-то в рамках std::set включен, и это заставляет GCC думать, что я пытаюсь объявить std::set вместо вызова функции-члена set.

GCC 4.7 выдает ошибку недопустимое использование 'class std:: set'

см. ниже пример, показывающий ошибка. Если вы прокомментируете using namespace std код компилируется нормально.

к сожалению, для меня невозможно пройти через всю кодовую базу, удалить каждый using namespace std вызов и префикс каждого вызова к чему-либо внутри пространства имен std с std::

есть ли другой способ обойти это?

#include <set>
using namespace std; // comment this out to compile fine

struct blah
{
    template<typename T>
    void set()
    { }
};

template<typename T>
struct base
{
    blah b;
};

template<typename T>
struct super : base<super<T>>
{
    void fun()
    {
        this->b.template set<int>(); // this line breaks
    }
};

int main()
{
    super<int> s;
    s.fun();

    return 0;
}

3 ответов


Ну, это довольно неловко для нас, толкателей C++.

это ошибка в G++, которая также появляется в тест-драйве Comeau. Это не изъян в самом языке. Проблема связана с характером синтаксического анализа слева направо и тем, как грамматика C++ обходит двусмысленность.

законно использовать шаблон не-члена, не-базового класса во вложенном спецификаторе имени для typedef для шаблона базового класса. В таком контексте шаблон класса без специальных отношение к доступному классу может появиться после ->:

#include <tuple>

template< typename t >
struct get_holder
    { typedef std::tuple< t > type; };

template< typename ... ts >
struct inherits
    : get_holder< ts >::type ... {

    inherits( ts ... v )
        : get_holder< ts >::type( v ) ...
        {}

    template< typename tn >
    void assign_one( inherits &o )
        { this->get_holder< tn >::type::operator= ( o ); } // <- here!
};

int main() {
    inherits< int, char, long > icl( 3, 'q', 2e8 );
    icl.assign_one< char >( icl );
}

так как C++ анализируется слева направо, когда анализатор попадает в ->, он должен разрешить get_holder прежде чем продолжить. Стандарт имеет специальное положение §3.4.5 / 1 [basic.уважать.classref] для этого:

в выражении доступа члена класса (5.2.5),если . или - > токен сразу же после идентификатора, за которым следует идентификатор должно быть, смотрели до определите, является ли

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

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

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

акцент мой-казалось бы, что G++ следует этой логике, несмотря на template ключевое слово, появляющееся между . и идентификатор set. Кроме того, предполагая, что он идет по этому маршруту, он должен был отметить двусмысленность как ошибку, а не пытаться выбрать нечлен.

в стандартной формулировке того, как действовать, когда template ключевое слово тут появляются, но это не должно вызвать хаос, который вы видите. §14.2 [temp.имена]:

когда имя специализации шаблона члена появляется после . или - > в постфиксном выражении или после спецификатора вложенного имени в квалифицированном идентификаторе, а постфиксное выражение или квалифицированный идентификатор явно зависят от параметра шаблона (14.6.2), но не относятся к члену текущего экземпляра (14.6.2.1), имя шаблона члена должно быть префиксом шаблона ключевого слова. в противном случае предполагается, что это имя не является шаблоном.

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

но намерение дизайна ясно, и вам не нужно добавлять искусственный вложенный спецификатор имени blah::, хотя это законное решение.


попробуйте это:

this->b.blah::template set<int>(); // this line breaks

у вас есть два варианта:

    this->b.blah::template set<int>(); 
     //     ^^^^^^ Be very explicit what set you want

кроме того, не используйте using namespace (самый лучший вариант).