Оператор меньше чем через неявное преобразование?

Рассмотрим следующий класс:

struct C 
{
     /* Class contents, without any arithmetic operator... */
     constexpr operator int() noexcept; // Implicit conversion to int
};

У меня вопрос:

  • Можно ли использовать C в стандартных алгоритмах, таких как std::sort, который в настоящее время использует оператор ‹по умолчанию?
  • Считается ли C удовлетворяющим концепции LessThanComparable?
  • Будет ли C соответствовать требованиям гипотетической концептуальной библиотеки алгоритмов, которая требует, чтобы тип был LessThanComparable.

person Vincent    schedule 29.12.2015    source источник
comment
Мне было любопытно, и я попробовал. Работал над C ++ 14 здесь: cpp.sh/4hdh   -  person Mehrdad    schedule 29.12.2015


Ответы (3)


Можно ли C использовать в стандартных алгоритмах, таких как std::sort, который в настоящее время использует оператор < по умолчанию?

Да, это работает для std::sort() и некоторых других стандартных алгоритмов. Код

#include <algorithm>
#include <vector>

struct C 
{
     /* Class contents, without any arithmetic operator... */
     constexpr operator int() noexcept {return 0;} // Implicit conversion to int
};

int main()
{
    std::vector<C> v;  
    std::sort( begin(v), end(v) );
}

компилирует. Вот живая демонстрация. Тем не менее, обратите внимание на следующий вопрос!

Считается ли C удовлетворяющим концепции LessThanComparable?

Нет. Требования концепции LessThanComparable заключаются в том, что для объектов x и y типа C или const C выражение x<y является действительным и неявно конвертируется в bool, а оператор < устанавливает строгое слабое отношение упорядочения. В вашем случае объекты const не будут преобразованы в ints. Это ошибка в вашем коде, потому что она некорректна. Добавление ключевого слова const заставит его работать, и класс C действительно будет LessThanComparable. Отношение строгого слабого порядка выполняется, потому что ints удовлетворяют этому требованию.

Будет ли C соответствовать требованиям гипотетической концептуальной библиотеки алгоритмов, которая требует, чтобы тип был LessThanComparable.

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

Несколько примечаний на полях:

  • GCC 4.9 компилирует x<y, даже если x и y относятся к типу const C. Похоже, это ошибка компилятора, поскольку GCC 5.2 и clang 3.6 выдают здесь ошибку времени компиляции.

  • Передача std::less<C>() в качестве дополнительного аргумента для std::sort() дает ошибку времени компиляции, потому что в этом случае функция сравнения требует, чтобы константные объекты были сопоставимы. Однако передача std::less<void>() ничего не ломает, поскольку аргументы передаются безупречно.

  • std::sort() алгоритм требует не полного LessThanComparable, а концепции Compare. Кроме того, тип итератора должен быть RandomAccessIterator, то есть ValueSwappable, а разыменованный тип должен быть MoveContructable и MoveAssignable. Это все, что касается вашего первого вопроса, даже если ошибка константности не исправлена. Вот почему работают std::sort() и другие стандартные алгоритмы.

person Ralph Tandetzky    schedule 29.12.2015
comment
О первом пункте: тот факт, что код компилируется и работает на практике, не означает, что такое поведение предписано стандартом. - person chi; 29.12.2015
comment
@chi Согласен. Вот почему в конце моего ответа было добавлено примечание. Я считаю, что все правильные реализации стандарта должны компилировать данный код. - person Ralph Tandetzky; 29.12.2015

Нет. Компилятор не может творить такую ​​большую магию, то есть вызывать метод приведения и затем применять оператор <. Представьте, что есть несколько операторов приведения для разных типов, как компилятор выберет подходящий?

РЕДАКТИРОВАТЬ: На самом деле это неправильно. Пока есть оператор одиночного приведения, это будет работать. Но с двумя или более компилятор будет жаловаться на неоднозначное приведение. Однако этот подход очень хрупкий, поэтому в целом это не лучшая идея.

person Zbynek Vyskovsky - kvr000    schedule 29.12.2015
comment
Я не могу согласиться с вами, ну не полностью. Компилятор вызовет приведение перед применением оператора. Это произойдет, потому что оператор - это просто функция, и если объект определил метод приведения или неявный конструктор с одним аргументом, он будет приведен в соответствие с этим оператором. Совершенно другая тема - неоднозначность нескольких операторов приведения. - person Glapa; 29.12.2015
comment
@Glapa: Вы правы, я проверил, и все заработало. Пока я не добавил еще один оператор приведения. Однако я обновил ответ. - person Zbynek Vyskovsky - kvr000; 29.12.2015

Я попробовал пример, предложенный mehrdad momeny. Все работало нормально. Однако после небольшого редактирования он больше не работает.

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

struct C 
{
    C(int x):X(x){}
    operator int() { return X; }
    operator float() { return static_cast<float>(X); }

    int X;
};

using namespace std;

int main()
{
    vector<C> u = {1, 2, 35, 6, 3, 7, 8, 9, 10};
    sort(u.begin(), u.end());
    for(auto x: u){
        cout << x << endl;
    }
}

Живая демонстрация

Потому что это приведет к двусмысленности. Так что делать это так - не лучшая идея.

person Humam Helfawi    schedule 29.12.2015
comment
Итак, гарантированно ли будет работать, если есть только один оператор преобразования? - person eerorika; 29.12.2015
comment
Я не имею никакого отношения к этому, а не к примеру с Мехрдадом момены. Вам стоит покопаться в стандарте - person Humam Helfawi; 29.12.2015