0

It makes sense to compare an object of my class only with the literal 0, how can I limit the value of the argument of the overridden comparison operators to this value for checking at compile time?

It is required to get the behavior as in the example

class C {
    public:
    bool operator>(zero_literal_t n);
    bool operator<(zero_literal_t n);
    bool operator==(zero_literal_t n);
    bool operator!=(zero_literal_t n);
    bool operator>=(zero_literal_t n);
    bool operator<=(zero_literal_t n);
};

int main()
{
    C c;
    int i0 = 0;
    int i1 = 1;
    auto v1 = c > 0; // Ok
    auto v2 = c > 1; // Compile time error
    auto v3 = c == 0; // Ok
    auto v4 = c == 1; // Compile time error
    auto v5 = c == i0; // Compile time error
    auto v6 = c == i1; // Compile time error

    return 0;
}
Maxim
  • 167
  • 5
  • 5
    Create an operator for comparing to [`std::nullptr_t`](https://en.cppreference.com/w/cpp/types/nullptr_t)? The integer constant literal `0` is implicitly convertible to a null pointer. – Some programmer dude Aug 01 '23 at 06:26
  • 1
    I think providing a conversion to `bool` might make more sense. – molbdnilo Aug 01 '23 at 06:38
  • Or compare against integral_constant if you really want to compare against a literal. Depends what you want really. – alagner Aug 01 '23 at 06:47
  • @Someprogrammerdude It works, although the signature can be misleading – Maxim Aug 01 '23 at 07:11
  • @molbdnilo Casting to bool will not work, because all comparison operators must be supported – Maxim Aug 01 '23 at 07:12
  • 1
    @Maxim Why? If you have wacky requirements, you should mention them in the question. (Also, I did not mention casting.) – molbdnilo Aug 01 '23 at 07:28
  • Might it make sense to just add a `is_zero` function? – Bob__ Aug 01 '23 at 07:52
  • @alagner I completed the question with what I need, how can the operator argument be compared with integral_constant? – Maxim Aug 01 '23 at 08:04
  • @molbdnilo I added a question – Maxim Aug 01 '23 at 08:07
  • @Bob__ I will do this if it is not possible to add an operator, since it is more cumbersome – Maxim Aug 01 '23 at 08:08
  • I meant `operator==(const T&, integral_constant<0, int>`. If you need comparison with normal int that option is no more viable. – alagner Aug 01 '23 at 08:09
  • @Maxim `std::nullptr_t`, `std::nullopt_t`, `std::monostate`, and `std::integral_constant<0, int>` are all types that only have one possible value, you can use any of them, or one of your own – Caleth Aug 01 '23 at 08:13
  • BTW, which version of the Standard are you targetting? Are you allowed to use the [three-way comparison operator](https://stackoverflow.com/questions/47466358/what-is-the-spaceship-three-way-comparison-operator-in-c)? – Bob__ Aug 01 '23 at 08:14
  • @alagner `bool operator==(C const& c, std::integral_constant n) { // }` does not work if you pass the literal 0 as an argument – Maxim Aug 01 '23 at 08:24
  • @user207421 Class instance cannot be compared to numbers other than 0 – Maxim Aug 01 '23 at 08:27
  • You said that. Everybody asked you why. You haven't answered that. – user207421 Aug 01 '23 at 08:28
  • @user207421 For an object, comparison with 0 serves as a check for the occupancy level, while it is impossible to determine the occupancy level relative to another number – Maxim Aug 01 '23 at 08:31
  • @Bob__ I'm targeting the C++17 version of the standard – Maxim Aug 01 '23 at 08:34
  • Groan. Define 'occupancy level'. Any chance you could make the effort to explain yourself properly? – user207421 Aug 01 '23 at 08:47
  • Would it be acceptable to use an user defined literal (say `0_ol`) instead of `0`? – Bob__ Aug 01 '23 at 08:57
  • @Bob__ I think not, since you have to rewrite the existing code and the option with functions in this case looks more convenient to use, since the development environment can suggest the required method, and you will have to look for the constant somewhere. – Maxim Aug 01 '23 at 09:27
  • @Maxim In `auto v6 = c == i0;` you are comparing `c` with a runtime value (`i0`) but expect a compile time error. How would that work? For runtime values you need a runtime check. [example](https://godbolt.org/z/b9W1osvYT) – Ted Lyngmo Aug 28 '23 at 17:07
  • @TedLyngmo I assumed that it could be done to work the same way as validating template arguments – Maxim Aug 30 '23 at 05:27
  • 1
    Template parameters are not and can not be runtime values. – Ted Lyngmo Aug 30 '23 at 05:56
  • @TedLyngmo Raildex's answer allows you to get compilation errors in this case – Maxim Aug 30 '23 at 06:09
  • @Maxim No, that requires you to change `i0` to a constant expression. It doesn't work with your definition of `i0`. – Ted Lyngmo Aug 30 '23 at 06:18
  • @TedLyngmo It does not compile for me, so all the conditions are met https://godbolt.org/z/zWEfMEs37 – Maxim Aug 30 '23 at 08:04
  • @Maxim I think you need to look at the reasons for _why_ that is not compiling. Anyway, if the answer given by Raildex answers your question, you could accept it instead of leaving the question open. – Ted Lyngmo Aug 30 '23 at 08:15
  • @TedLyngmo The compilation error occurs because you cannot implicitly cast int to nullptr_t. I was hoping there was a way to not use nullptr_t because the interface becomes confusing. – Maxim Aug 30 '23 at 08:21
  • I'm confused about what you need if you say the conditions _are_ met by Raildex's solution but then it still fails with an `int` that is actually `0`. The main question remains: Do you really need to check runtime values? If you do, you will not be able to get a compilation error. – Ted Lyngmo Aug 30 '23 at 09:07
  • @TedLyngmo I just wanted to know if there is a way to define comparison operators with a literal 0 so that when they are called with an argument other than the literal 0, a compilation error occurs. It is with the literal 0, and not with the variable that stores the number 0. – Maxim Aug 30 '23 at 09:53
  • 1
    Ok, then if `nullptr_t` is confusing, hide it in a type called `Zero`: [Example](https://godbolt.org/z/87T3Ke944) – Ted Lyngmo Aug 30 '23 at 10:17
  • @TedLyngmo I agree, but I think it's better to use using https://godbolt.org/z/anh3asbYj – Maxim Aug 31 '23 at 06:35
  • 1
    @Maxim Perhaps. It depends on what kind of error message you want. Making `Zero` a real type results in `no known conversion for argument 1 from 'int' to 'Zero'` and making it an alias may produce `no known conversion for argument 1 from 'int' to 'C::Zero' {aka 'std::nullptr_t'}` which may be a bit confusing. – Ted Lyngmo Aug 31 '23 at 07:01

1 Answers1

3

You can write an operator== that takes a std::nullptr_t.
the integer constant literal 0 is implicitly convertible to std::nullptr_t

However, this can only happen at compile time and only if there is no overload that takes an int.

#include <cstddef>
struct Object {
    int i;
    bool operator==(const std::nullptr_t t) {
        return i == 0;
    };

    bool operator==(const Object& t) {
        return i == t.i;
    };

    /*
    bool operator==(const int t) {
        return i == t;
    };
    */
};


int main() {
    Object o{0};
    Object o2{1};
    bool a = o == 0;
    bool b = o == o2;

    const int i = 0;
    bool c = o == i; // will either not compile or will use the int overload
}
Raildex
  • 3,406
  • 1
  • 18
  • 42