Разное поведение компиляторов для специализации шаблонов - PullRequest
2 голосов
/ 04 апреля 2019

Я обнаружил различное поведение компиляторов при попытке специализировать шаблон. Вот минимальный код, который иллюстрирует проблему:

#include <iostream>

template<typename A, typename B>
struct foo {
   static const bool value = false;
};

template<typename B>
struct foo<int, B> {
   static const bool value = !foo<B, B>::value;
};

int main() {
   std::cout << foo<int, int>::value << std::endl;
   return 0;
}

У меня есть общий шаблон с двумя параметрами и специализированный шаблон для первого параметра типа int. При использовании компилятора g ++ я получаю

main.cpp: In instantiation of 'const bool foo<int, int>::value':
main.cpp:10:30:   recursively required from 'const bool foo<int, int>::value' 
main.cpp:10:30:   required from 'const bool foo<int, int>::value'
main.cpp:14:32:   required from here 
main.cpp:10:30: fatal error: template instantiation depth exceeds maximum of 900 (use -ftemplate-depth= to increase the maximum)
    static const bool value = !foo<B, B>::value;

, поскольку компилятор использовал value из специализированной версии и получается бесконечная рекурсия. Я получаю

Error   C2131   expression did not evaluate to a constant
Error   C2065   'value': undeclared identifier  

для MSVC. Но с clang или zapcc код компилируется без ошибок. Какова причина? Каково правильное поведение в этом случае в соответствии со стандартом или поведение не определено?

1 Ответ

3 голосов
/ 04 апреля 2019

Я не адвокат по языку, но это должно не компилироваться. Вы пытаетесь инициализировать значение с самим собой во время компиляции. Я предполагаю, что это лягушатник. На самом деле, я не понимаю, почему GCC должен следовать по пути рекурсии, если он попадает в цикл.

Кстати, MSVC говорит вам (GodBolt):

<source>(14): note: see reference to class template instantiation 'foo<int,int>' being compiled

что является правильным уловом. На этот раз MSVC затмевает другие компиляторы ...: -P

edit: @aschepler отмечает, что мы получаем (GodBolt) такое же поведение даже без шаблонов:

struct bar { static const bool value = !bar::value; };

другое редактирование : Кажется, еще несколько лет назад это был своего рода действительный код из-за "софистики языка юриста". Видите ли, стандарт раньше говорил (Раздел 6.6.2 [basic.start.static] параграф 2):

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

и clang принял это, чтобы означать, что сначала вы все инициализируете нулями, а затем учитываете инициализации статической длительности - это означает, что bar::value неявно decltype(bar::value) { 0 } перед его собственной явной инициализацией. Это было изменено после сообщения о дефекте 2026 .

Кредит за указание на это идет Ричард Смит .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...