Почему SFINAE портится при изменении места специализации шаблона класса? Это ошибка C++?
Следующий код дает ожидаемую ошибку компилятора ( Демо):
1 template<bool> struct Range;
2
3 template<int value, typename = Range<true> > struct Unique;
4 template<int value> struct Unique<value, Range<(value > 1)> > { typedef char type[1]; };
5 template<int value> struct Unique<value, Range<(value > 2)> > { typedef char type[2]; };
6
7 Unique<3>::type o1;
8 Unique<3>::type o2;
Теперь, если я поменяю местами строку 5 и строку 7. Тогда нет ошибки компилятора! Демо
5 Unique<3>::type o1;
7 template<int value> struct Unique<value, Range<(value > 2)> > { typedef char type[2]; };
За o1
Понятно, что ошибок нет, потому что специализация на (value > 2)
еще не видно Но почему нет ошибки для o2
также, который видит 2 соответствующих специализации!
Я думаю, что компилятор должен выбрать Unique<3>::type
с некоторым произвольным именем, когда он встречается в первый раз, а затем заменяет Unique<3>::type
везде с этим именем.
Это ошибка компиляции, или ошибка C++, или "особенность" C++?
3 ответа
В 14.5.5.1 Сопоставление частичных специализаций шаблона класса есть
Если найдено более одной подходящей специализации, правила частичного порядка (14.5.5.2) используются для определения того, является ли одна из специализаций более специализированной, чем другие. Если ни одна из специализаций не является более специализированной, чем все другие соответствующие специализации, то использование шаблона класса является неоднозначным, и программа является плохо сформированной.
Однако это применимо только к первому случаю, когда видны две специализации, и я пока не уверен, что эти две специализации действительны сами по себе.
Однако во втором случае до достижения второй специализации идентификатор шаблона Unique<3>
уже существует, для которого (благодаря Н.М., Матье М., Джеймсу Канзе) уже создана первая специализация:
14.5.5 Частичные специализации шаблона класса
Частичная специализация должна быть объявлена перед первым использованием специализации шаблона класса, которая будет использовать частичную специализацию как результат неявной или явной реализации в каждой единице перевода, в которой такое использование происходит; Диагностика не требуется.
А в 14.5.5, пункт 8
В списке аргументов частичной специализации шаблона класса применяются следующие ограничения:
- Частично специализированное нетипичное выражение аргумента не должно включать параметр шаблона частичной специализации, кроме случаев, когда выражение аргумента является простым идентификатором. [ > Пример:
template <int I, int J> struct A {};
template <int I> struct A<I+5, I*2> {}; // error
template <int I, int J> struct B {};
template <int I> struct B<I, I> {}; // OK
- конец примера]
Таким образом, кажется, что нетипичные аргументы не участвуют в создании специализации, если не используются в качестве простого идентификатора (таким образом, Range<(value > 2)>
было бы неправильно).
Так что, похоже, ваш код не правильно сформирован.
Не связано напрямую, но все же интересно в этом отношении:
14.7.3 Явная специализация
Размещение явных объявлений специализации для шаблонов функций, шаблонов классов, функций-членов шаблонов классов, статических элементов-членов шаблонов классов, классов-членов шаблонов классов, шаблонов классов-членов шаблонов классов, шаблонов функций-членов шаблонов классов, функций-членов-членов шаблоны шаблонов классов, функции-члены шаблонов элементов не шаблонных классов, шаблоны функций-членов классов-членов шаблонов классов и т. д., а также размещение объявлений частичной специализации шаблонов классов, шаблоны классов-членов классов, не являющихся шаблонами, член Шаблоны классов Шаблоны классов и т. д. могут влиять на правильность формирования программы в соответствии с относительным расположением явных объявлений специализации и их точек создания в единице перевода, как указано выше и ниже. При написании специализации будьте осторожны с ее местоположением; или сделать его компиляцией будет таким испытанием, чтобы разжечь его самосожжение.
Шаблон создается в первый раз, когда он необходим (в модуле перевода), а не каждый раз.
o1
не видит вторую специализацию из-за этого:
14.5.5 / 1 Частичная специализация должна быть объявлена до первого использования специализации шаблона класса, которая будет использовать частичную специализацию в результате неявной или явной реализации в каждой единице перевода, в которой такое использование происходит; Диагностика не требуется.
Во втором примере вторая специализация будет использоваться в реализации Unique<3>
если бы это было видно до объявления o1
, Поскольку это правило нарушается, программа нарушается, и компилятору разрешается об этом молчать.
o2
не видит вторую специализацию, потому что вообще не видит никакой специализации. Его класс создается один раз, в точке o1
декларация.