C ++ Concepts / SFINAE: разные результаты clang и MSVC / G ++ для определения внешней функции с шаблоном и Concept / SFINAE

Я определяю конструкторы класса, который ограничен проверкой эквивалентности типа базового типа переданного итератора по сравнению с node_type, определенным в моем классе, с помощью концепций или SFINAE.

Однако, попробовав несколько комбинаций с концепцией is_same<>, is_same_v<>, enable_if<> и same_as<>, определение вне линии всегда будет приводить к ошибкам времени компиляции по крайней мере в одном из основных компиляторов. В настоящее время нижеприведенный фрагмент не видит ошибок на MSVC / g ++, но clang жалуется, все с использованием последней версии (19.28, 11.1, 12.0 соответственно). Кто-нибудь может прояснить, что здесь не так? Спасибо.

Отредактировано:

  • Код, как показано ниже, на Godbolt с использованием C ++ 20, MSVC / g ++ ok, ошибка clang https://godbolt.org/z/fdqEYxKh8

  • Код не показан ниже, но на godbolt с использованием C ++ 11 SFINAE, MSVC / clang ok, ошибка g ++ https://godbolt.org/z/7nr7a8jY8

#include <stdio.h>
#include <iterator>
#include <array>
#include <concepts>
#include <cstdlib>

template <typename FPType, std::size_t N>
class PointND {
private:
    std::array<FPType, N> coords_;
};

template <typename FPType, std::size_t N, typename ElemType>
class Tree {
public:

    struct node_type {
        PointND<FPType, N> key;
        ElemType value;
    };
    
    template <std::random_access_iterator RAI>
    requires std::same_as<typename std::iterator_traits<RAI>::value_type, 
                          typename Tree<FPType, N, ElemType>::node_type>
    Tree(RAI, RAI);
};

template <typename FPType, std::size_t N, typename ElemType>
template <std::random_access_iterator RAI> 
requires std::same_as<typename std::iterator_traits<RAI>::value_type, 
                      typename Tree<FPType, N, ElemType>::node_type>
Tree<FPType, N, ElemType>::Tree(RAI begin, RAI end) {
}

Ошибка в этой строке: 45: 30: ошибка: вне строки определение «Tree‹ FPType, N, ElemType ›» не соответствует никакому объявлению в «Tree‹ FPType, N, ElemType ›»

Tree<FPType, N, ElemType>::Tree(RAI begin, RAI end) {

person Nkk    schedule 14.06.2021    source источник
comment
_Tp делает вашу программу плохо сформированной. Начиная с _, заглавная буква зарезервирована для использования компилятором только (и встроена в библиотеки). (Вы должны это исправить, но это не является причиной ошибки вашего компилятора)   -  person Yakk - Adam Nevraumont    schedule 14.06.2021
comment
Кстати, почему тег C ++ 11?   -  person Enlico    schedule 14.06.2021
comment
@Enlico Поскольку я пробовал использовать способ c ++ 11 enable_if ‹is_same‹ ››, clang компилируется, а msvc и g ++ - нет.   -  person Nkk    schedule 14.06.2021
comment
@ Yakk-AdamNevraumont, спасибо за совет, исправленный в реализации.   -  person Nkk    schedule 14.06.2021
comment
@Nkk, я не думаю, что это причина использовать этот тег. enable_if все еще жив и здоров в C ++ 20. Я думаю, вам следует удалить тег, который читается как Используйте этот тег для кода, который должен компилироваться как C ++ 11. Требуется ли компиляция как C ++ 11? Если нет, то снимите метку.   -  person Enlico    schedule 14.06.2021
comment
@Enlico, я добавил строгую демку C ++ 11, g ++ жалуется.   -  person Nkk    schedule 14.06.2021
comment
Рассмотрите возможность регистрации ошибок для этих компиляторов.   -  person HolyBlackCat    schedule 14.06.2021


Ответы (1)


В примере C ++ 11 добавьте -fchecking, и gcc скажет:

<source>:52:51: internal compiler error: canonical types differ for identical types 'std::enable_if<(std::is_same<typename std::iterator_traits<_InputIterator>::iterator_category, std::random_access_iterator_tag>::value && std::is_same<typename std::iterator_traits<_InputIterator>::value_type, typename Tree<FPType, N, ElemType>::node_type>::value), int>' and 'std::enable_if<(std::is_same<typename std::iterator_traits<_InputIterator>::iterator_category, std::random_access_iterator_tag>::value && std::is_same<typename std::iterator_traits<_InputIterator>::value_type, Tree<FPType, N, ElemType>::node_type>::value), int>'
   52 | Tree<FPType, N, ElemType>::Tree(RAI begin, RAI end) {
      |                                                   ^
...
Please submit a full bug report,
with preprocessed source if appropriate.
Please include the complete backtrace with any bug report.
See <https://gcc.gnu.org/bugs/> for instructions.

Ошибка компиляции gcc - это ошибка компилятора.

Я подозреваю, но не могу доказать, что поведение clang в коде C ++ 20 также является ошибкой.

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

person Jeff Garrett    schedule 14.06.2021
comment
Перемещение вложенного класса за пределы - это скорее дизайнерское решение с нежелательными эффектами, чем тривиальное изменение, скажем, псевдонима. Один простой пример, когда два компилятора потерпели неудачу, если они доказаны, заслуживают большего внимания, чем просто обходные пути. Но спасибо за вклад. - person Nkk; 14.06.2021