1

I am so confused, and I'm sorry if this is obvious. Am I wrong that in the following:

 struct MyStruct
    {
       MyStruct(){};
       MyStruct(MyStruct* arg){};
    }

MyStruct(MyStruct* arg){}; is a constructor taking one pointer to a MyStruct as argument?

Because I have a problem that this constructor (which I think it is) is being called when I do this:

int main()
{
   MyStruct obj;
   MyStruct* objPtr;
   obj = objPtr;

   return 0;
} 

When assigning obj to objPtr I expected the compiler to complain, but it doesn't, and instead calls MyStruct(MyStruct* arg); which I thought was a constructor taking a pointer argument.

Any help would be appreciated. Also, if I add a copy assignment operator to the class it still happens.

Edit: Thanks for the answers. It looks like I've got some reading to do on this, and the topic seems to be (for anyone wondering) converting constructors in C++. Also I'm guessing the explicit keyword. Here is a link to an SO question which explains it:

What is a converting constructor in C++ ? What is it for?

Community
  • 1
  • 1
Zebrafish
  • 11,682
  • 3
  • 43
  • 119

4 Answers4

6
  1. The compiler synthesizes an assignment operator for you:

    MyStruct& MyStruct::operator=(MyStruct const&) = default;
    
  2. When it sees the assignment, it finds a candidate for the operator (which is the one it created). Then it sees that it can use your constructor to make a conversion, to a type that will allow the assignment (MyStruct). So it boils down to:

    obj = MyStruct (objPtr);
    

If you want to see the error happen, mark you constructor as explicit:

struct MyStruct
{
   MyStruct(){};
   explicit MyStruct(MyStruct* arg){};
}
StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • Perhaps comment that if the non-default constructor was `explicit`, this wouldn't happen. - Oh! You just did ! – Martin Bonner supports Monica Nov 09 '16 at 08:42
  • 1
    @MartinBonner, you mean the one I just added? – StoryTeller - Unslander Monica Nov 09 '16 at 08:43
  • This is confusing. So the compiler sees that the assignment operator takes a reference to a MyStruct, and it sees a pointer, and instead of saying it's a mismatch it constructs a new object after the equals sign with the pointer as an argument to the constructor. That's very confusing. Is this normal? Am I right to be confused? Does this thing have a particular name so I can look it up? Wow. And thanks. – Zebrafish Nov 09 '16 at 08:56
  • @TitoneMaurice, yes it's normal (both the behavior and your confusion). The c++ standard specifies that the compiler will do up to one user defined *implicit type conversion* (this is the term you should look for) for each function parameter, as part of function overload resolution. – StoryTeller - Unslander Monica Nov 09 '16 at 09:05
1

For obj = objPtr;, the complier will try to call MyStruct::operator=() on obj with argument objPtr, which is MyStruct*. There's a candidate, the implicitly declared copy assignment operator MyStruct::operator=(const MyStruct&). MyStruct could be converted from MyStruct* via converter constructor MyStruct::MyStruct(MyStruct*), so it compiles.

If you make the MyStruct::MyStruct(MyStruct*) explicit, compile will fail.

struct MyStruct
{
   MyStruct(){};
   explicit MyStruct(MyStruct* arg){};
};
songyuanyao
  • 169,198
  • 16
  • 310
  • 405
1

When you sasign objPtr to obj, you're assigning a value of type MyStruct* to MyStruct - that's invalid. However, since you have a constructor that takes MyStruct*, it's called to convert the value. It's basically an implicit conversion :)

Luaan
  • 62,244
  • 7
  • 97
  • 116
  • Thanks. I think get the gist of it. My understanding is that it sees the assignment operator takes a reference to the struct, and when it sees a pointer to the struct it creates an entirely new object of that struct, passing the pointer to the constructor and placing it after the equals sign. I'm very confused over this. When you say it calls the single pointer argument constructor to "convert" the value, wouldn't it make more sense to just dereference the pointer to "convert" it? I mean, creating an entirely new object you haven't specified seems very counterintuitive. – Zebrafish Nov 09 '16 at 09:01
  • @TitoneMaurice Not quite. On the left side, you have a `MyStruct` - not a reference, just a value. On the right side, you have `MyStruct*`. `MyStruct*` is *not* `MyStruct`. If you didn't have the constructor, you'd do something like `obj = *objPtr` (which would of course cause undefined behaviour in your sample). Pointer to A isn't A, it's a pointer. You provided an implicit conversion operator, so the compiler used it. That's what implicit conversion operators are for. If you didn't provide it, you would get a compiler error - again, you're trying to assign a pointer to a *different type*. – Luaan Nov 09 '16 at 12:21
1
obj = objPtr;

Will call

obj.MyStruct::operator =(MyStruct(objPtr));

Mark your constructor explicit to avoid this type of unwanted conversion.

Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • Do you have an example of this that makes the behavior seem helpful? I'm curious what the language developers were thinking. – The Nate Nov 09 '16 at 09:49
  • @TheNate Sounds like pretty typical OOP to me. If there's a default method that takes A and returns B, and you have an instance of A and want an instance of B, the default method is called. Just because C++ (and most other languages) call the default method a constructor doesn't make it special. It's not like the designer of C++ had to make a conscious decision on this - it's a standard in SmallTalk, and C++ was influenced in no small part by SmallTalk. ML languages use this today as well (e.g. F#'s `None` as a shortcut for `Option None`). – Luaan Nov 09 '16 at 12:25