25

The output of the code below in VS2015 is "constructor".

Shouldn't it fail to compile due to the missing assignment operator?

struct A { };

struct B {
    B(){}
    B(const A& a) {
        cout << "constructor" << endl;
    }

    //B& operator=(const A& a) {
    //  cout << "assignment operator" << endl;
    //  return *this;
    //}
};

int main() {
    A a;
    B b;
    b = a;

    return 0;
}
CuriousGeorge
  • 6,446
  • 5
  • 35
  • 66
  • 1
    This will be of use to you: http://stackoverflow.com/questions/3734247/what-are-all-the-member-functions-created-by-compiler-for-a-class-does-that-hap – SenselessCoder Nov 07 '16 at 01:51
  • But those don't handle any conversion, like from A to B, right? – CuriousGeorge Nov 07 '16 at 01:52
  • Oh I see it now, I thought it was one of those were B was inheriting from A. Apologies. In this case, I think there's something weird going on. This might be a compiler thing. – SenselessCoder Nov 07 '16 at 01:54

3 Answers3

30

Yes, when there is a conversion going on, like in your testcase.

You're effectively calling

b = B(a);

Because B's assignment operator B& operator=(B const&) is implicitly declared, it is found during overload resolution. Because your assignment is only one conversion away from being a match (and that's exactly the number of conversions that are allowed to happen), it converts a to B and then assigns the new temporary B to b.

Quentin
  • 58,778
  • 7
  • 120
  • 175
krzaq
  • 15,660
  • 3
  • 41
  • 59
14

Let's consider an analogous example.

double a;
int b=5;

a=b;

The only thing you can assign to a double is another double. However, an int can be converted to a double.

Similarly here, A can be converted to B, because the constructor to do so exists. And that's what happens.

Sam Varshavchik
  • 84,126
  • 5
  • 57
  • 106
  • 2
    Well, you can assign an `int` to a `double`. The defined behaviour of this is that the `int` is implicitly converted to `double` and the result of that conversion replaces the stored value of the `double`. Maybe it would be better to say "The only thing that can be stored in a `double` variable is a `double` value", or something. – M.M Nov 07 '16 at 03:37
11

Your code has implicit conversion from A to B, the b = a is compiled as b = B(a);. If you want this to be detected as error, you can use the explicit specifier:

struct B {
    B(){}
    explicit B(const A& a) {
        std::cout << "constructor" << std::endl;
    }
};

Then you should get error, such as these generated by ideone.com:

prog.cpp: In function 'int main()':
prog.cpp:20:7: error: no match for 'operator=' (operand types are 'B' and 'A')
     b = a;
       ^
prog.cpp:5:8: note: candidate: B& B::operator=(const B&)
 struct B {
        ^
prog.cpp:5:8: note:   no known conversion for argument 1 from 'A' to 'const B&'
prog.cpp:5:8: note: candidate: B& B::operator=(B&&)
prog.cpp:5:8: note:   no known conversion for argument 1 from 'A' to 'B&&'

After that, the constructor will never be implicitly called, and if you want to call it, you have to write it explicitly: b = B(a);

hyde
  • 50,653
  • 19
  • 110
  • 158