0

I'm sure this question exists elsewhere, but can't find an answer. I've noticed that when a constructor is defined as deleted, it makes that type take part in overload resolution. In other words, if you were to remove the deleted constructor entirely, the overload resolution would not even consider that type.

Here is some simple code. Hopefully I'm showing this correctly, because I haven't tried such a simple case yet:

struct SomeType { .... SomeType(const void*)=delete; .... };
void DoStuff(const void *ptr);
void DoStuff(const SomeType &obj);

SomeType test_obj;
DoStuff( test_obj );

If SomeType has a deleted constructor defined as SomeType(const void*)=delete;, the compiler will complain about overload ambiguity. Why is this? And is there any way to stop this behavior? Doesn't it make more sense for a deleted function to have the opposite effect? To ensure that it doesn't take part in overload resolution?

The only way I've found to fix this issue is to declare the constructor as explicit:

explicit SomeType(const void*)=delete;

This appears to delete the constructor and conversions. Is this the correct way to deal with this situation? Or am I missing something? Thanks!

Note: I'm using Visual Studio 2022 with v142 build tools and C++20

Edit: Okay, my sample code compiles without issues, so I'm thinking maybe this is only an issue if the types have other conversions. While the sample code doesn't directly demonstrate the problem, it shows a simpler version of my actual code. I will continue to mess around to see if I can figure out exactly what causes the ambiguity error to get thrown. But if anyone can give me some advice on why a deleted function would cause ambiguity, that would be great.

Robert
  • 413
  • 4
  • 12
  • What would be the point of `SomeType(const void*)=delete;` if it behaved exactly the same as not writing the line at all? `explicit` doesn't save you btw. It still participates in overload resolution. However `explicit` constructors are not considered in copy-initialization and implicit conversion sequences for function arguments. – user17732522 May 08 '22 at 15:43
  • The alleged [ambiguity cannot be reproduced from the code you have provided](https://gcc.godbolt.org/z/r3xM5vT4v). Even passing a `void*` directly to `DoStuff` works. – Nicol Bolas May 08 '22 at 15:45
  • Apologies. I attempted to convert a complex situation into a simple one, but failed to capture all of the properties exactly. – Robert May 08 '22 at 15:47
  • @Robert: "*But if anyone can give me some advice on why a deleted function would cause ambiguity, that would be great.*" That's found in the duplicate Q&A. – Nicol Bolas May 08 '22 at 15:47
  • The reason for the deleted constructor is to block input of a specific type that can convert to other acceptable constructor types. The two types are associated with each other, but one cannot be fed into the other. I'm assuming that is what causes the ambiguity. – Robert May 08 '22 at 15:52
  • I looked through the "duplicate" thread, but didn't find any reasonable explanation of why a deleted constructor would cause overload ambiguity. In my case, my goal is to prevent one type from being converted into another. But when I attempt to do this, it makes the situation worse instead of better. Rather than block the conversion, it gives errors that make it appear that I'm trying to allow the conversion. And for whatever reason, the explicit keyword on the deleted constructor fixes it. All of this makes it seem like a deleted constructor is being mistaken for a valid conversion. – Robert May 08 '22 at 16:07
  • @Robert As far as overload resolution is concerned a constructor is a valid conversion if it is not marked `explicit`, whether or not it is deleted. Adding constructors can always cause previously unambiguous overload resolution to be become ambiguous, but that is not the case with the shown code. I recommend you boil down your code to a proper [mre] and then someone might be able to explain to you why overload resolution has the result that it has in your case and whether there is a better approach. In any case `= delete` should make no difference whatsoever to that. – user17732522 May 08 '22 at 16:12
  • A deleted constructor participates in the candidate selection process. If determined to be the best choice (and admit I find the rules a bit convoluted), it'll fail because it is deleted. If determined to one of multiple best choices, it'll fail because it is ambiguous. This behavior is by careful and intentional design. – Eljay May 08 '22 at 17:29

0 Answers0