简体   繁体   中英

binary operator overloading, implicit type conversion

class my_bool {
  private:
    bool value;
  public:
    my_bool(bool value) : value(value) {}
    explicit operator bool() { return value };

    friend my_bool operator==(const my_bool & instance_1, const my_bool & instance_2);
    friend my_bool operator&&(const my_bool & instance_1, const my_bool & instance_2);

};

void main(){
  my_bool a = true;
  bool b = false;

  if(a == b){
    // do something
  }

  if(a && b){
    // do something 
  }
}

I have just created a similar topic regarding my problem over here binary operator overloading; implicit type conversion . It can be deleted I guess because it is not explicit enough about the problem I am encountering.

Why is that operator== works fine and operator&& causes ambiguities? How do I solve this problem? I can surely write down two more overloads of operator&& ( bool , my_bool ), ( my_bool , bool ). That is a messy solution though

The builtin operator&& is a context in which expressions are contextually converted to bool . Other such contexts include for example the condition of if , for , while and the conditional operator ? .

Quoting N4296, §4/4 (the latest publicly available draft before C++14):

Certain language constructs require that an expression be converted to a Boolean value. An expression e appearing in such a context is said to be contextually converted to bool and is well-formed if and only if the declaration bool t(e); is well-formed, for some invented temporary variable t (8.5).

Basically, this means that there is an "impicit explicit conversion to bool " in these contexts. Or, to illustrate this further, you can think of the following two lines being one and the same:

a && b
static_cast<bool>(a) && static_cast<bool>(b)

Therefore, the compiler must consider the explicit operator bool() when doing overload resolution for operator&& , but has to ignore it when doing overload resolution for operator== (since that operator does not force a " bool context" .. you can also compare numbers, strings, ...).


The solution in your case is IMO to get rid of the operator&&(const my_bool&, const my_bool&) all together. After all, it does not produce a more meaningful behavior than what would be possible by relying on the builtin operator&&(bool, bool) . Establishing a second "boolean context" just isn't something the language was designed for (see below).

If you want to keep this operator, say for some side effects, then I see these choices:

  • Be explicit at the call site. That is:

     if (static_cast<my_bool>(a) && static_cast<my_bool>(b)) { /* ... */ } 
  • Be explicit at the definition site: Provide additional definitions for operator&&(my_bool const &, bool) , operator&&(bool, my_bool const &) . These then should rule out both operator&&(my_bool const &, my_bool const &) as well as operator&&(bool, bool) because the later are less specific. Adding these definitions to your class should mitigate the issue :

     friend my_bool operator&&(const my_bool & lhs, bool rhs) { // Delegate to operator&&(const my_bool &, const my_bool &) return lhs && my_bool(rhs); } friend my_bool operator&&(bool lhs, const my_bool & rhs) { // Delegate to operator&&(const my_bool &, const my_bool &) return my_bool(lhs) && rhs; } 

Turns out one can "establish a boolean context", using CRTP:

#include <iostream>
using namespace std;

template<typename T>
struct bool_context {
    friend T operator&&(T const & lhs, bool rhs) {
        return lhs && T(rhs);
    }
    friend T operator&&(bool lhs, T const & rhs) {
        return T(lhs) && rhs;
    }
    friend T operator||(T const & lhs, bool rhs) {
        return lhs || T(rhs);
    }
    friend T operator||(bool lhs, T const & rhs) {
        return T(lhs) || rhs;
    }
};

struct my_bool : bool_context<my_bool> {
    bool value;
    my_bool(bool v) : value(v) {}
    explicit operator bool() { return value; };
    friend my_bool operator&&(my_bool const & lhs, my_bool const & rhs) {
        cout << "my_bool::operator&&" << endl;
        return lhs.value && rhs.value;
    }
    friend my_bool operator||(my_bool const & lhs, my_bool const & rhs) {
        cout << "my_bool::operator||" << endl;
        return lhs.value || rhs.value;
    }
};


int main(int, char**) {
    my_bool a = true;
    bool b = false;
    cout << "a && b => "; a && b; // my_bool::operator&&
    cout << "b && a => "; b && a; // my_bool::operator&&
    cout << "a && a => "; a && a; // my_bool::operator&&
    cout << "b && b => "; b && b; cout << endl;
    cout << "a || b => "; a || b; // my_bool::operator||
    cout << "b || a => "; b || a; // my_bool::operator||
    cout << "a || a => "; a || a; // my_bool::operator||
    cout << "b || b => "; b || b; cout << endl;
    return 0;
}

( Ideone )

My first thought on this is that the arguments to the compiler's built-in operator&& are (bool, bool) , so my_bool's explicit bool operator can be invoked - since you are in effect, requesting an explicit conversion.

However, I can't find any reference in the standard as to whether a variable appearing on the right hand side of && should invoke an explicit conversion to bool.

Here's the complete error output from apple clang (once the source code above is fixed):

./nod.cpp:45:10: error: use of overloaded operator '&&' is ambiguous (with operand types 'my_bool' and 'bool')
    if(a && b){
       ~ ^  ~
./nod.cpp:33:20: note: candidate function
    friend my_bool operator&&(const my_bool & instance_1, const my_bool & instance_2);
                   ^
./nod.cpp:45:10: note: built-in candidate operator&&(_Bool, _Bool)
    if(a && b){
         ^
1 error generated.

So how do I fix it?

Remove the user-defined && operator.

class my_bool {
private:
    bool value;
public:
    my_bool(bool value) : value(value) {}
    explicit operator bool() { return value; }

    friend my_bool operator==(const my_bool & instance_1, const my_bool & instance_2);
//    friend my_bool operator&&(const my_bool & instance_1, const my_bool & instance_2);

};

int main(){
    my_bool a = true;
    bool b = false;

    if(a == b){
        // do something
    }

    if(a && b){
        // do something
    }
}

The code bellow works in the same manner for both == and &&. The class equality is triggered only when are of the same type.

#include <stdio.h>

class my_bool {
private:
  bool v{false};
  public:
    my_bool() : v(v) {};
    operator bool (){return v;}

    friend bool operator==(const my_bool a, my_bool b){
            printf("operator==\n");return a.v==b;
        }
        friend bool operator&&(const my_bool a, my_bool b){
            printf("operator&&\n");return a.v&&b;
        }
};

int main(int argc, char **argv)
{ printf("Starting\n");
  bool a=true,b=true;
  my_bool A{},B{},R{};

  a==b;a&&b;
  a==A;a&&A;
  A==b;A&&b;
  A==B;A&&B;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM