g++, полученный из класса с параметром шаблона

рассмотрим следующий код:

template<class T>
class Base {
  public:
    void doSomething(){}
};

template<class T>
class Derived : public Base<T> {
  public:
    void doMore() {
      doSomething(); //Affected line
    }
};

В строке, прокомментированной «затронутой строкой», g++ (4.7) говорит:

test.cc:11:16: error: there are no arguments to ‘doSomething’ that depend on a template parameter, so a declaration of ‘doSomething’ must be available [-fpermissive]

Теперь мне интересно:

  • Если бы параметра шаблона T не было, этой ошибки не возникло бы. В чем разница?
  • g++, очевидно, может решить эту проблему (если я добавлю -fpermissive, он отлично скомпилируется). Я предполагаю, что g++ пытается сделать для меня наилучший опыт как для "пользователя" (программиста). Каковы преимущества для меня, когда g++ не принимает этот код?

Спасибо! Натан


person Nathan    schedule 29.05.2012    source источник
comment
Измените на this->doSomething() (или Base<T>::doSomething()), так как doSomething() зависит от типа T.   -  person hmjd    schedule 29.05.2012
comment
@hmjd: это интересно .. можете кратко описать, как помогает добавление this->?   -  person Asha    schedule 29.05.2012
comment
См. этот вопрос и там ответы.   -  person juanchopanza    schedule 29.05.2012
comment
@Asha, this-> сообщает компилятору, что имя является членом, поэтому поиск имени откладывается до фазы 2 (создание экземпляра), когда известен тип базового класса и видны объявления его членов.   -  person Jonathan Wakely    schedule 29.05.2012


Ответы (2)


Это описано в GCC Подробная диагностика вики-страница.

Каковы преимущества для меня, если g++ не принимает этот код?

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

person Jonathan Wakely    schedule 29.05.2012
comment
Хорошо, это соответствует стандарту. Но я должен сказать, что меня раздражает, что стандарт заставляет меня писать this-›doSomething() или использовать -fpermissive, и я не понимаю, почему стандарт не может быть лучше. - person Nathan; 29.05.2012
comment
Если вы настроите Base<int> так, чтобы Base<int>::doSomething был элементом данных, а не функцией-членом, то создание экземпляра Derived<int>::doMore будет недопустимым, поскольку он попытается вызвать элемент данных. Таким образом, тип имен в базовом классе или существуют ли они вообще, не может быть известен до момента создания экземпляра, поэтому нет смысла искать в базовом классе до создания экземпляра. Чтобы сообщить компилятору, что имя может исходить от базового класса, и дождаться создания экземпляра для выполнения поиска, вы должны уточнить имя или использовать this->, иначе поиск не сможет его найти. - person Jonathan Wakely; 29.05.2012
comment
... альтернативой компилятору было бы иногда находить его во время определения, а затем иногда удалять, если его нет во время создания экземпляра, что было бы непоследовательным и еще более запутанным. Если вы хотите, чтобы непоследовательность иногда работала так, а иногда иначе, попробуйте PHP ;-) Стандарт попытается дать единый, недвусмысленный набор правил о том, как компилируются шаблоны, и одним из следствий является что в шаблонах поиск неквалифицированного имени никогда не смотрит в зависимых базовых классах. - person Jonathan Wakely; 29.05.2012

Если вы не добавляете this или Base<T>, вы пишете код, который не соответствует стандарту - GCC хочет помешать вам это сделать. См. также запись журнала изменений для GCC 4.7:

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

template <class T>
void f() { g(T()); } // error, g(int) not found by argument-dependent lookup
void g(int) { } // fix by moving this declaration before the declaration of f

template <class T>
struct A: T {
  // error, B::g(B) not found by argument-dependent lookup
  void f() { g(T()); } // fix by using this->g or A::g
};

struct B { void g(B); };

int main()
{
  f<int>();
  A<B>().f();
}

(найдено здесь: http://gcc.gnu.org/gcc-4.7/changes.html ).

person cschwan    schedule 29.05.2012
comment
GCC отклонил пример OP, начиная с по крайней мере 4.1, это изменение в 4.7 не связано с поиском имени в зависимых базах. - person Jonathan Wakely; 29.05.2012