2

Why is implicit type conversion not performed for user-defined type in the following source code?

An implicit type conversion to type A should occur on the commented line, but it did not happen and an error occurred on that line.

I would like to know the grammatical rules and solutions for this error.

#include <iostream>

using namespace std;

class A {
   int v;
public:
    A(int _v): v(_v) {}
    operator int (void) const {
        return v;
    }
    friend ostream& operator << (ostream& os, const A& s) {
        return os << "A: " << s.v;
    }   
};

class B {
    int x, y;   
public:
    B(int _x, int _y): x(_x), y(_y) {}
    operator A(void) const {
        return A(x+y);
    }
};

int main(void)
{
    B b(1,2);
    cout << A(b) << endl;
    cout << (A)b << endl;
    cout << b << endl;     // error --> why?
    return 0;
}

--> Additional Question

Thanks for Sam Varshavchik's reply .

Defining class A as below solves the problem.

class A {
   int v;
public:
    A(int _v): v(_v) {}
    operator int (void) const {
        return v;
    }
    friend ostream& operator << (ostream& os, const A& s);
};

ostream& operator << (ostream& os, const A& s){
    return os << "A: " << s.v;
}

//////////////

class A {
   int v;
public:
    A(int _v): v(_v) {}
    operator int (void) const {
        return v;
    }
    friend ostream& operator << (ostream& os, const A& s){
        return os << "A: " << s.v;
    }
};

ostream& operator << (ostream& os, const A& s);

Whether or not to declare "operator <<" as a friend doesn't seem to matter here. Since the function is a global function, it can be referenced in other classes or other funcitons. I think it matters whether the block defining the function is inside or outside class A. Why is function declaration necessary when using inner definition? I want to know the grammatical basis.

mzeff
  • 21
  • 3
  • I see where you are going here and you probably know this already, but for future readers, be careful with the C-style cast `(A)b`. A C-style cast will threat `b` as an `A` whether it makes sense or not. Here it makes sense. It won't always.This would be a good place for a `static_cast`. `static_cast` makes sure the cast makes sense. – user4581301 Mar 15 '20 at 17:36
  • What is the error message? Hint: it probably says that the conversion is ambiguous. – Pete Becker Mar 15 '20 at 17:42
  • I also use static_cast in the actual development process. I hope students who see this code will remember the advice of user4581301. However, if you still don't know about static_cast, you can skip his advice. This code is only for beginners. – mzeff Mar 15 '20 at 18:07
  • to Pete Becker: This is not an error due to conversion ambiguity. Thanks for your attention. – mzeff Mar 15 '20 at 18:09

2 Answers2

3

This must have something to do with arcane rules related to the relationship between overload resolution, friend functions, and implicit conversions. Because this is not just about implicit conversions, here. You're also defining an overload operator<< that's a friend function.

By simply getting rid of the friend function, the following code compiles fine, on gcc 9.2. It also doesn't hurt to get rid of using namespace std;, as well:

#include <iostream>

class A {
   int v;
public:
    A(int _v): v(_v) {}
    operator int (void) const {
        return v;
    }
};

std::ostream& operator << (std::ostream& os, const A& s) {
    return os << "A: " << (int)s;
}

class B {
    int x, y;
public:
    B(int _x, int _y): x(_x), y(_y) {}
    operator A(void) const {
        return A(x+y);
    }
};

int main(void)
{
    B b(1,2);
    std::cout << A(b) << std::endl;
    std::cout << (A)b << std::endl;
    std::cout << b << std::endl;     // No more error
    return 0;
}
Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148
  • Using "std ::" on all objects can increase the length of the code, making it less readable. I think it is good for readability to omit the "std", which is the namespace of the standard library, even if other namespaces are omitted. – mzeff Mar 15 '20 at 18:22
  • Should I find for you a few questions on stackoverflow.com where someone was utterly confused by an utterly incomprehensible compilation error whose only, sole, cause was `using namespace std;`? I keep answering them, for years... [Here's one](https://stackoverflow.com/questions/37127824/i-am-trying-to-use-templates-in-c/37127907). You're welcome to do a mental excersize of trying to figure out what's the reason for that compilation error, without knowing this in advance. – Sam Varshavchik Mar 15 '20 at 18:29
  • You don't have to find those questions again for me. I think the answer to a similar mistake each time can make you tired. I think you are a good person who is sorry for making such mistakes and trying not to make it happen. And your efforts will definitely help develop the world. – mzeff Mar 15 '20 at 19:15
  • However, even if it is easy for beginners to make mistakes, it is very important to improve the readability of the code. Even if your question is flooded by some beginners' immature use of namespaces, it doesn't seem like a clever idea not to take advantage of the C ++ syntax. Rather, it is a better way to explain to them how to use namespaces correctly. – mzeff Mar 15 '20 at 19:15
2

When a friend function is defined inside a class it will only be found through ADL.

In your first two lines you are providing an A, so ADL kicks in and finds the operator<<. When passing a B ADL will not help find your operator<< so a conversion is never considered by the compiler.

Some more details on ADL.

super
  • 12,335
  • 2
  • 19
  • 29