16

Consider the following class:

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

My question is:

  • Is C usable in standard algorithms like std::sort that currently uses the default < operator?
  • Is C considered as satisfying the LessThanComparable concept?
  • Will C meet the requirements of an hypothetical conceptified algorithm library that would require the type to be LessThanComparable.
Vincent
  • 57,703
  • 61
  • 205
  • 388

3 Answers3

12

Is C usable in standard algorithms like std::sort that currently uses the default < operator?

Yes, it works for std::sort() and some other standard algorithms. The code

#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) );
}

compiles. Here's a live demo. Look at the next question though!

Is C considered as satisfying the LessThanComparable concept?

No. The requirements of the LessThanComparable concept are, that for objects x and y of type C or const C the expression x<y is valid and implicitly convertible to bool and the < operator establishes a strict weak ordering relation. In your case const objects do will not convert to ints. This is a bug in your code, because it is not const correct. Adding the const keyword will make it work and the class C would indeed be LessThanComparable. The strict weak ordering relation is fulfilled, because ints fulfil this requirement.

Will C meet the requirements of an hypothetical conceptified algorithm library that would require the type to be LessThanComparable.

If you fix your constness, yes, it will.

A few sidenotes:

  • GCC 4.9 compiles x<y even if x and y are of type const C. This seems to be a compiler bug, since GCC 5.2 and clang 3.6 throw a compile time error here.

  • Passing std::less<C>() as an extra argument to std::sort() gives a compile time error, because the compare function requires constant objects to be comparable in that case. However, passing std::less<void>() doesn't break anything, since arguments are perfectly forwarded.

  • The std::sort() algorithm does not require a full LessThanComparable, but the concept Compare. Furthermore, the iterator type must be a RandomAccessIterator that is ValueSwappable and the dereferenced type must be MoveContructable and MoveAssignable. This is all the case for your first question, even when the constness bug is not fixed. That's why std::sort() and other standard algorithms work.

Ralph Tandetzky
  • 22,780
  • 11
  • 73
  • 120
  • 1
    About the first point: the fact that the code compiles and works in practice does not imply that such behavior is mandated by the standard. – chi Dec 29 '15 at 13:09
  • 1
    @chi I agree. That's why a added a sidenote at the end of my answer. I believe that all correct implementations of the standard must compile the given code. – Ralph Tandetzky Dec 29 '15 at 13:13
3

No. Compiler cannot do so big magic, i.e. calling the cast method and then applying < operator. Imagine there are several cast operators for different types, how would the compiler choose the proper one?

EDIT: Actually it's not correct. As long as there is single cast operator, this will work. But with two or more the compiler will complain about ambiguous cast. However, this approach is highly fragile so in general it's not good idea.

Zbynek Vyskovsky - kvr000
  • 18,186
  • 3
  • 35
  • 43
  • 2
    I can't agree with you, well not completely. Compiler will call cast before applying operator. That will happen because operator is "just a function" and if object has defined cast method or implicit constructor with one argument it will be casted to match this operator. Ambiguity of several cast operators is totally different topic. – Glapa Dec 29 '15 at 09:41
  • @Glapa: You're right, I checked and it worked. As long as I haven't added another cast operator. I updated the answer though. – Zbynek Vyskovsky - kvr000 Dec 29 '15 at 09:45
2

I tried the example proposed by mehrdad momeny. It worked fine. However, with little editing, it is not working anymore.

#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;
    }
}

Live Demo

Because this will lead to an ambiguity. So, it is not a good idea to to do it like this.

Community
  • 1
  • 1
Humam Helfawi
  • 19,566
  • 15
  • 85
  • 160