Почему GCC нужны дополнительные объявления в шаблонах, когда VS не делает?

template<typename T>
class Base
{
protected:
    Base() {}
    T& get() { return t; }
    T t;
};

template<typename T>
class Derived : public Base<T>
{
public:
    Base<T>::get;                    // Line A
    Base<T>::t;                      // Line B
    void foo() { t = 4; get(); }
};

int main() { return 0; }

Если я комментирую строки A и B, этот код компиляции, прекрасные в соответствии с Visual Studio 2008. Все же, когда я компилирую под GCC 4.1 со строками A и прокомментированный B, я получаю эти ошибки:

В функции членства, ‘пусто Полученной:: нечто ()’:
ошибка: ‘t’ не был объявлен в этом объеме
ошибка: нет никаких аргументов для 'добираний', которые зависят от шаблонного параметра, таким образом, объявление 'добирается', должно быть доступным

Почему один компилятор потребовал бы строк A и B, в то время как другой не делает? Существует ли способ упростить это? Другими словами, если производные классы используют 20 вещей от базового класса, я должен поместить 20 строк объявлений для каждого класса, происходящего из Основы! Существует ли путь вокруг этого, которое не требует такого количества объявлений?

16
задан Kyle 11 May 2010 в 23:01
поделиться

2 ответа

GCC в данном случае прав, а Visual Studio ошибочно принимает неправильно сформированную программу. Посмотрите раздел Поиск имени в руководстве по GCC. Paraphrasing:

[T]вызов [get()] не зависит от аргументов шаблона (нет аргументов, зависящих от типа T, и также не указано, что вызов должен быть в [шаблонно-]зависимом контексте). Таким образом, глобальное объявление такой функции должно быть доступно, поскольку объявление в базовом классе не видно до момента инстанцирования.

Вы можете обойти это одним из трех способов:

  • Объявления, которые вы уже используете.
  • Base::get()
  • this->get()

(Есть и четвертый способ, если вы хотите поддаться Темной стороне:

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

Но я бы не рекомендовал этого делать, как по причине, указанной в руководстве, так и по причине того, что ваш код все равно будет недействительным C++.)

.
15
ответ дан 30 November 2019 в 22:09
поделиться

Проблема не в gcc, а в Visual Studio, которая принимает код, не соответствующий стандарту C ++.

На этот вопрос был дан полный ответ на этом самом сайте, так что я буду краток.

Стандарт требует, чтобы шаблоны оценивались дважды:

  • один раз в точке определения: template struct Foo {void bar (); };
  • один раз в момент создания экземпляра: Foo myFoo;

В первый раз все независимые имена должны быть вычтены из контекста:

  • компилятор выдаст, если вы забыли пунктуацию, относится к неизвестным типам / методам / атрибутам
  • компилятор выберет перегрузку для функций, задействованных на этом этапе

Поскольку синтаксис C ++ неоднозначен, необходимо помочь синтаксическому анализатору на этом этапе и использовать ключевые слова template и typename , чтобы устранить неоднозначность вручную.

К сожалению, Visual Studio несовместима и реализует только вторую оценку (в момент создания экземпляра). Преимущество для ленивых в том, что вы можете обойтись без лишних ключевых слов template и typename , недостатком является то, что ваш код плохо сформирован и не переносится ...

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

void foo(int) { std::cout << "int" << std::endl; }

template <class T> void tfoo(T i) { foo(i); }

void foo(double) { std::cout << "double" << std::endl; }

int main(int argc, char* argv[])
{
  double myDouble = 0.0;
  tfoo(myDouble);
  return 0;
}

Скомпилированный с помощью gcc, он выводит int .

Скомпилировано с помощью Visual Studio, выводит double .

Проблема? Любой, кто повторно использует тот же символ, который вы используете в своем коде шаблона в VS, может наступить вам на ногу и испортить вашу реализацию, если его символ появится между включением вашего кода шаблона и моментом, когда они фактически используют код шаблона ... не так ли смешно: /?

Теперь для вашего кода:

template<typename T>
class Derived : public Base<T>
{
public:
  void foo() { this->t = 4; this->get(); }
};

this указывает, что следующее имя является зависимым именем , т.е. оно зависит от T (что не очевидно, когда символ появляется отдельно). Поэтому компилятор будет ждать создания экземпляра и проверять, содержит ли конкретный тип, который вы создаете, шаблон Base эти методы. Это не обязательно, так как я мог бы идеально специализировать Base :

// It's non-sensical to instanciate a void value,
template <>
class Base<void> {};

И поэтому Derived не должен компилироваться;)

6
ответ дан 30 November 2019 в 22:09
поделиться
Другие вопросы по тегам:

Похожие вопросы: