Search code examples
c++c++11default-constructor

Ambiguity in default constructor


I'm trying to understand some of the default options for constructors introduced in C++11, so I was reading some notes from Bjarne.

On this page, he was the following example:

struct Z {
    // ...

    Z(long long);     // can initialize with an long long
    Z(long) = delete; // but not anything less
};

He notes that Z can be constructed with a long long, but not anything less. This phrasing is a little unclear to me. I assumed "anything less" might refer to anything that occupies less space than a long long.

I tested some sizes on my machine and found that long long requires 8 bytes and a long double requires 16 bytes, so I assumed that constructing with a long double would be fine, and would simply truncate the fractional part and coerce it to a long long.

I wrote this:

class Foo {
  public:
    Foo(long long x) : x(x) {}
    Foo(long x) = delete;
  private:
    long long x;
};

int main() {
  Foo foo1(100LL); // Compiles fine
  // Foo foo2(100L); // Compile error - cannot construct with smaller than `long long`. This is obvious
  Foo foo3(42.0L); // This causes an error

  return 0;
}

To my surprise, the above code resulted in an error:

error: call of overloaded 'Foo(long double)' is ambiguous Foo foo3(42.0L);

This seems unambiguous to me. Why can the compiler not resolve this?


Solution

  • A deleted constructor is declared, but not defined. Instead of undefined, it is deleted.

    And when the compiler does function overload resolution, it will use the collection of declared compatible functions, that includes deleted functions too. If the overload resolution happens to select a deleted function, then you'll get a compiler error. And if the overload resolution cannot decide the best function, you'll also get a compiler error, even if the ambiguity comes from deleted functions.

    You might think that it would be better that deleted functions do not take part in overload resolution. But that would be quite less useful. If you don't want Foo(long) to participate in overload resolution, just do not write it!

    This is similar to overload with public/private functions. Even when calling from outside the class, public and private functions participate in overload resolution:

    class Foo
    {
    public:
        void fun(long long);
    private:
        void fun(long);
    };
    int main()
    {
        Foo f;
        f.fun(100LL); //ok
        f.fun(100L); //error: fun(long) is private
        f.fun(1.0); //error: call is ambiguous
    }