Search code examples
c++explicitobject-construction

explicit deleted constructors - does this matter?


When marking a constructor as deleted, i.e.

class MyClass {

   // ... 

   MyClass(int x) = delete;

   // ... 

};

is there any effect to marking the deleted constructor as explicit? i.e.

   explicit MyClass(int x) = delete;

?


Solution

  • Yes, = delete does not affect overload resolution, except that if in the end overload resolution would choose an overload that is deleted, then the overload resolution will be considered ill-formed. = delete does not modify the decision making process for overload resolution.

    explicit does affect overload resolution substantially in that it changes whether the constructor will participate as candidate in different overload resolution scenarios.

    The two constructs are orthogonal. One does not imply the other should be added or dropped.


    Example:

    struct MyClass {
        MyClass(int x) = delete;
        MyClass(long x) { };
    };
    

    Without explicit on the first constructor, MyClass x = 0; is ill-formed, because overload resolution would prefer the first constructor. With explicit the first constructor is not considered for the copy-initialization and the second one (which otherwise would be a worse match) is chosen. The initialization will not be ill-formed.

    On the other hand, for an initialization of the form MyClass x(0);, which is direct-initialization, all constructors are always considered and overload resolution will choose the first one, regardless of whether it is explicit or not. Therefore this initialization will always be ill-formed.

    So, in this example, you need to decide whether you want to make only explicit conversion from int ill-formed (while still allowing implicit and explicit conversion from long) or whether you want to make implicit and explicit conversion from long ill-formed.


    Even if this is the only constructor for the class, there is still a difference.

    Suppose there is a function overload set

    void f(MyClass);
    void f(A);
    

    with

    struct MyClass {
        explicit MyClass(int x) = delete;
    };
    
    struct A {
        A(int x) {};
    };
    

    Then trying to call f(0) is well-formed, because the first overload is non-viable. explicit constructors are not considered for implicit conversion sequences for function arguments in overload resolution. Only the second overload is viable and will be chosen.

    But if there is no explicit, then both overloads become viable and the overload resolution will be ambiguous. That the constructor is deleted doesn't matter for this determination.


    Deleting a constructor means that you want a specific overload resolution result to cause the initialization to be ill-formed. When making the decision which form you want to be ill-formed you always have to consider whether or not explicit should be applied, just like when defining non-deleted overloads.