Недопустимый шаблон зависимой функции-члена шаблона вычета-думает, что я пытаюсь использовать 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>();
// ^^^^^^ Be very explicit what set you want
кроме того, не используйте using namespace
(самый лучший вариант).