Подтвердить что ты не робот

Что проверяет компилятор на наличие незакрепленного кода шаблона?

Например, следующий фрагмент кода компилируется с помощью gcc-4.9 и clang-602

class Base                                                                      
{                                                                               
public:                                                                         
    static void foo() {}                                                        
    void badfoo(int i) {}                                                       
};                                                                              

template <typename T>                                                           
class Derived : public Base                                                     
{                                                                               
public:                                                                         
    void bar() { Base::foo(); }                                                 
    void badbar() { Base::badfoo(); }  // compiles ok
    //static void badbar() { Base::badfoo(); }  // compile error                                                                                    
    //void worsebar() { Base::nonexist(); }  // compile error                                   
};                                                                              

int main()                                                                      
{                                                                               
    return 0;                                                                   
}  

Но прокомментированные строки не будут компилироваться.

Мои вопросы:

  • Почему badbar() компилируется, но worsebar() не работает?

  • Если я изменяю badbar() на статичный, он не будет компилироваться, независимо от того, является ли base::badfoo статическим или нет.

  • Знает ли стандарт что-либо о том, что следует проверять в этой ситуации? gcc4.4 фактически отказывается компилировать даже badbar().

Обновление:

Проблема 1 объясняется рядом ответов, но, похоже, компиляторы иногда идут на лишнюю милю, чтобы проверить перегрузку, это происходит с gcc 4.4.3 и 4.8.2, но не 4.7.2 и 4.9.1.

Проблема 2: Как заметил Марко А., clang не будет компилироваться, но gcc4.9 все равно пройдет. Однако gcc4.2 и gcc4.4 оба отклоняют код, а ошибка, которую они жалуются, - это "не соответствующая функция", а не "вызов нестатического элемента без объекта" в clang. Кажется, нет никакого окончательного ответа на этот вопрос, поэтому я добавляю тег-адвокат языка, предложенный Даниэлем Фрейем.

Больше обновлений:

Я думаю, что для вопроса 2 ответ довольно ясен: до компилятора вопрос о том, будет ли добавление статической декларации изменить диагноз. Он варьируется от компилятора до компилятора и разных версий одного и того же компилятора. Языковой адвокат не появился, я собираюсь принять ответ Даниэля Фрея, поскольку он лучше всего объяснил первый вопрос. Но ответы от Marco A. и Hadi Brais также заслуживают внимания.

4b9b3361

Ответ 1

Стандарт требует, чтобы поиск имени выполнялся на этапе 1, когда шаблон сначала разбирался. Это вызывает badfoo в badbar, поэтому код компилируется. Компилятор не должен выполнять разрешение перегрузки в это время.

Разрешение перегрузки (которое всегда происходит как отдельный шаг после поиска имени) затем выполняется на этапе 2 при создании badbar - это не так в вашем примере. Этот принцип можно найти в

3.4 Поиск имени [basic.lookup]

1 Правила поиска имен применяются равномерно ко всем именам (включая typedef-names (7.1.3), namespace-names (7.3) и имена классов (9.1)), где грамматика позволяет такие имена в контексте, обсуждаемом конкретным правилом. Поиск имени связывает использование имени с объявлением (3.1) этого имени. Поиск имени должен найти однозначное объявление для имени (см. 10.2). Поиск имени может связывать более одного объявления с именем, если он считает имя именем функции; говорят, что объявления образуют набор перегруженных функций (13.1). Разрешение перегрузки (13.3) происходит после успешного поиска имени. Правила доступа (раздел 11) рассматриваются только после успешного поиска имени и разрешения перегрузки функции (если применимо). Только после поиска имени, функция перегрузки разрешения (если применимо) и проверки доступа были успешными, являются атрибуты, введенные в объявлении имен, используемых далее в обработке выражений (раздел 5).

(Акцент мой)

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

Чтобы отклонить код , вам нужно создать экземпляр badbar.

Ответ 2

Рассмотрим [temp.res]/8:

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

Это (в частности, бит "без необходимости диагностики" ) делает поведение компилятора совместимым с worsebar. Различия в реализации этого типа кода - это только вопросы QoI - обычные компиляторы делают некоторый анализ и будут жаловаться. Трудно сказать, когда именно, и вы должны быть готовы вернуться к шаблону кода при обновлении или переключении своей реализации.

Ответ 3

Чтобы сделать некоторую ясность.. эта версия кода компилируется просто отлично на clang и gcc

class Base                                                                      
{                                                                               
public:                                                                         
    static void foo() {}                                                        
    void badfoo(int a) {}                                                       
};                                                                              

template <typename T>                                                           
class Derived : public Base                                                     
{                                                                               
public:                                                                         
    void bar() { Base::foo(); }                                                 
    void badbar() { Base::badfoo(); }   
}; 

так

[temp.res]/P8

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

Оба gcc и clang не требуются для диагностики этого. Это также относится к тому же случаю, что и выше (clang испускает ошибку, gcc не делает)

class Base                                                                      
{                                                                               
public:                                                                         
    static void foo() {}                                                        
    void badfoo(int a) {}                                                   
};                                                                              

template <typename T>                                                           
class Derived : public Base                                                     
{                                                                               
public:                                                                         
    void bar() { Base::foo(); }                                                 
    static void badbar() { Base::badfoo(); } 
}; 

Случай с

void worsebar() { Base::nonexist(); }

отличается от , он нарушает поиск имени [temp.res]/p9

При поиске объявления имени, используемого в определении шаблона, обычные правила поиска (3.4.1, 3.4.2) используются для не зависимых имен

и [temp.res]/p10

Если имя не зависит от параметра шаблона (как определено в 14.6.2), объявление (или набор объявлений) поскольку это имя должно быть в области действия в точке, где имя отображается в определении шаблона

Отказ от ответственности: все вышеперечисленное не относится к MSVC, который с радостью откладывает все это на вторую фазу поиска.


Edit:

в случае

class Base                                                                      
{                                                                               
public:                                                                         
    static void foo() {}                                                        
    void badfoo(int i) {}                                                       
};                                                                              

template <typename T>                                                           
class Derived : public Base                                                     
{                                                                               
public:                                                                         
    static void badbar() { Base::badfoo(); }  // static function

clang вызывает ошибку, в то время как gcc не, Это относится к первому случаю, так как поиск имени успешный, но clang выполняет дополнительную проверку: поскольку badfoo является функцией-членом, он пытается построить правильное неявное ссылочное выражение элемента. Затем он уловит тот факт, что функция-член неявно вызывается из статической функции и обнаруживает несоответствие контекста. Эта диагностика полностью зависит от компилятора на данный момент (это не было бы в случае создания экземпляра).

Ответ 4

Прежде чем он создает экземпляры любых типов или испускает какой-либо код, компилятор постепенно создает таблицу всех объявленных символов. Если используется необъявленный символ, он испускает ошибку. Вот почему худший барабан не будет компилироваться. С другой стороны, badfoo был объявлен и так badbar компилируется. На этом раннем этапе процесса компиляции компилятор не будет проверять, действительно ли вызов badfoo соответствует объявленному badfoo.

Поскольку тип Derived не был инстанцирован нигде в коде, компилятор не выдаст никакого кода относительно него. В частности, badbar будет просто пренебрегать.

Теперь, когда вы объявляете экземпляр Derived (например, Derived <int> ), но без использования каких-либо его членов, компилятор просто создает тип с теми членами, которые были использованы и опускают другие. Тем не менее, нет ошибки в отношении badbar.

Однако при объявлении экземпляра Derived и вызова badbar потребуется создание метода badbar, и поэтому компилятор создаст тип с badbar и скомпилирует его. На этот раз компилятор замечает, что badfoo фактически не объявлен и поэтому испускает ошибку.

Это поведение описано в стандарте С++ в разделе 14.7.1.

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

Наконец, если badbar был статичным и был создан компилятором (потому что он был использован), то компилятор испустит ошибку, которая не существует. Теперь, если вы передадите целочисленный аргумент badfoo, выдается другая ошибка, указывающая, что статический метод не может получить доступ к члену экземпляра, потому что в нем нет экземпляра.

Edit

Компилятор не обязан НЕ сообщать о семантических ошибках в неизученных типах шаблонов. Стандарт просто говорит, что это не обязательно, но это возможно. Что касается того, где рисовать линию, открыта для обсуждения. См. это обсуждение связанной проблемы в clang:

какие непрошеные шаблоны мы анализируем? По соображениям производительности я не думаю, что мы должны анализировать все неизведанные шаблоны, так как мы можем многократно анализировать огромную часть Boost и STL и т.д.

Таким образом, неизменный анализ шаблонов изменяется с разными версиями clang и gcc по-разному. Но опять же, согласно стандарту: Нет необходимости сообщать об ошибках в неинсталлированных шаблонах, конечно.

Ответ 5

Сначала, если вы создадите экземпляр Devired и попытаетесь вызвать badbar, произошла ошибка компиляции:

// ...
int main()                                                                      
{                                                                               
    Derived<int> d;
    d.badbar();
    return 0;                                                                   
}

производит

error: too few arguments to function call, single argument 'i' was not specified
void badbar() { Base::badfoo(); }  // compiles ok
                ~~~~~~~~~~~~ ^

Компилятор не компилирует код, который не создается.