Нужно ли объявлять оператор < перед вызовом std::lexicographical_compare?

Следующий код компилируется до C++20:

#include <algorithm>
#include <vector>

struct Foo
{
};

struct FooArray
{
    std::vector<Foo> a;
};

bool operator<(const FooArray& a, const FooArray& b)
{
    return std::lexicographical_compare(std::begin(a.a), std::end(a.a), std::begin(b.a), std::end(b.a));
}

bool operator<(const Foo& a, const Foo& b)
{
    return false;
}

При включенном C++20 GCC 10 и 11 отклоняют код, но только тогда, когда включена оптимизация:

In file included from /opt/compiler-explorer/gcc-10.5.0/include/c++/10.5.0/bits/stl_algobase.h:71,
                 from /opt/compiler-explorer/gcc-10.5.0/include/c++/10.5.0/algorithm:61,
                 from <source>:1:
/opt/compiler-explorer/gcc-10.5.0/include/c++/10.5.0/bits/predefined_ops.h: In instantiation of 'constexpr bool __gnu_cxx::__ops::_Iter_less_iter::operator()(_Iterator1, _Iterator2) const [with _Iterator1 = const Foo*; _Iterator2 = const Foo*]':
/opt/compiler-explorer/gcc-10.5.0/include/c++/10.5.0/bits/stl_algobase.h:1240:14:   required from 'constexpr bool std::__lexicographical_compare_impl(_II1, _II1, _II2, _II2, _Compare) [with _II1 = const Foo*; _II2 = const Foo*; _Compare = __gnu_cxx::__ops::_Iter_less_iter]'
/opt/compiler-explorer/gcc-10.5.0/include/c++/10.5.0/bits/stl_algobase.h:1257:46:   required from 'static constexpr bool std::__lexicographical_compare<_BoolType>::__lc(_II1, _II1, _II2, _II2) [with _II1 = const Foo*; _II2 = const Foo*; bool _BoolType = false]'
/opt/compiler-explorer/gcc-10.5.0/include/c++/10.5.0/bits/stl_algobase.h:1302:60:   required from 'constexpr bool std::__lexicographical_compare_aux(_II1, _II1, _II2, _II2) [with _II1 = const Foo*; _II2 = const Foo*]'
/opt/compiler-explorer/gcc-10.5.0/include/c++/10.5.0/bits/stl_algobase.h:1605:48:   required from 'constexpr bool std::lexicographical_compare(_II1, _II1, _II2, _II2) [with _II1 = __gnu_cxx::__normal_iterator<const Foo*, std::vector<Foo> >; _II2 = __gnu_cxx::__normal_iterator<const Foo*, std::vector<Foo> >]'
<source>:15:40:   required from here
/opt/compiler-explorer/gcc-10.5.0/include/c++/10.5.0/bits/predefined_ops.h:43:23: error: no match for 'operator<' (operand types are 'const Foo' and 'const Foo')
   43 |       { return *__it1 < *__it2; }
      |                ~~~~~~~^~~~~~~~
In file included from /opt/compiler-explorer/gcc-10.5.0/include/c++/10.5.0/bits/stl_algobase.h:67,
                 from /opt/compiler-explorer/gcc-10.5.0/include/c++/10.5.0/algorithm:61,
                 from <source>:1:
/opt/compiler-explorer/gcc-10.5.0/include/c++/10.5.0/bits/stl_iterator.h:1097:5: note: candidate: 'template<class _IteratorL, class _IteratorR, class _Container> constexpr std::__detail::__synth3way_t<_IteratorR, _IteratorL> __gnu_cxx::operator<=>(const __gnu_cxx::__normal_iterator<_IteratorL, _Container>&, const __gnu_cxx::__normal_iterator<_IteratorR, _Container>&)' (reversed)
 1097 |     operator<=>(const __normal_iterator<_IteratorL, _Container>& __lhs,
      |     ^~~~~~~~
/opt/compiler-explorer/gcc-10.5.0/include/c++/10.5.0/bits/stl_iterator.h:1097:5: note:   template argument deduction/substitution failed:
In file included from /opt/compiler-explorer/gcc-10.5.0/include/c++/10.5.0/bits/stl_algobase.h:71,
                 from /opt/compiler-explorer/gcc-10.5.0/include/c++/10.5.0/algorithm:61,
                 from <source>:1:
/opt/compiler-explorer/gcc-10.5.0/include/c++/10.5.0/bits/predefined_ops.h:43:23: note:   'const Foo' is not derived from 'const __gnu_cxx::__normal_iterator<_IteratorL, _Container>'
   43 |       { return *__it1 < *__it2; }
      |                ~~~~~~~^~~~~~~~
In file included from /opt/compiler-explorer/gcc-10.5.0/include/c++/10.5.0/bits/stl_algobase.h:67,
                 from /opt/compiler-explorer/gcc-10.5.0/include/c++/10.5.0/algorithm:61,
                 from <source>:1:
/opt/compiler-explorer/gcc-10.5.0/include/c++/10.5.0/bits/stl_iterator.h:1114:5: note: candidate: 'template<class _Iterator, class _Container> constexpr std::__detail::__synth3way_t<_T1> __gnu_cxx::operator<=>(const __gnu_cxx::__normal_iterator<_Iterator, _Container>&, const __gnu_cxx::__normal_iterator<_Iterator, _Container>&)' (rewritten)
 1114 |     operator<=>(const __normal_iterator<_Iterator, _Container>& __lhs,
      |     ^~~~~~~~
/opt/compiler-explorer/gcc-10.5.0/include/c++/10.5.0/bits/stl_iterator.h:1114:5: note:   template argument deduction/substitution failed:
In file included from /opt/compiler-explorer/gcc-10.5.0/include/c++/10.5.0/bits/stl_algobase.h:71,
                 from /opt/compiler-explorer/gcc-10.5.0/include/c++/10.5.0/algorithm:61,
                 from <source>:1:
/opt/compiler-explorer/gcc-10.5.0/include/c++/10.5.0/bits/predefined_ops.h:43:23: note:   'const Foo' is not derived from 'const __gnu_cxx::__normal_iterator<_Iterator, _Container>'
   43 |       { return *__it1 < *__it2; }
      |                ~~~~~~~^~~~~~~~
<source>:13:6: note: candidate: 'bool operator<(const FooArray&, const FooArray&)'
   13 | bool operator<(const FooArray& a, const FooArray& b)
      |      ^~~~~~~~
<source>:13:32: note:   no known conversion for argument 1 from 'const Foo' to 'const FooArray&'
   13 | bool operator<(const FooArray& a, const FooArray& b)
      |                ~~~~~~~~~~~~~~~~^
Compiler returned: 1

GCC 12 и более поздние версии скомпилируют код. MSVC всегда компилирует код, clang отклоняет его, когда включен C++20.

https://godbolt.org/z/9ra6hn7c9

Какой компилятор правильный? Нужно ли объявлять bool operator<(const Foo& a, const Foo& b) перед вызовом std::lexicographical_compare? Это изменение в C++20 или код просто работает в C++17 (я был удивлен, что код не вызывает проблем и в C++17)?

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

user12002570 31.07.2024 09:47

GCC и MSVC, похоже, задерживают фактическое создание экземпляра до тех пор, пока не станет доступно объявление operator<. Но clang пытается создать экземпляр. Хотя, возможно, я здесь ошибаюсь.

user12002570 31.07.2024 09:50

Если порядок объявления именно такой, но определения выполняются отдельно, он компилируется, так что я так думаю.

Chukwujiobi Canon 31.07.2024 11:07

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

Alan Birtles 31.07.2024 11:20

@AlanBirtles Как только вы поймете, что такое стандартное поведение, на ваш вопрос будет дан ответ. Другой вопрос, правильно ли это реализует компилятор или показывает регрессию.

user12002570 31.07.2024 11:23
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
6
82
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это неправильно сформированный запрос, согласно temp.param#7 диагностика не требуется.

Прежде всего обратите внимание, что существует две точки создания экземпляров (POI). Первый POI находится в области пространства имен сразу после operator<(const FooArray& a, const FooArray& b) согласно temp.point:

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

Второй POI находится в конце единицы перевода.

Теперь согласно temp.point#7:

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

Это означает, что данная программа является IFNDR, поскольку она нарушает Basic.def.odr:

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

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


Решение состоит в том, чтобы предоставить объявление перед первым POI, чтобы упомянутые функции были одинаковыми в обоих POI.

Другие вопросы по теме