4

It would appear that overload resolution functions slightly differently for imported constructors and explicitly declared constructors but I do not understand why.

Consider the following code:

struct BaseBase { int val; };

struct Base : BaseBase
{
    int test;

    Base() : BaseBase{}, test(0) {}
    Base(const Base& other) : BaseBase(other), test(1) {}
    Base(const BaseBase& other) : BaseBase(other), test(2) {}
};

struct Derived : Base
{
#if 1
    Derived(const Base& other) : Base(other) {}
    Derived(const BaseBase& other) : Base(other) {}
#else
    using Base::Base;
#endif
};

int foo()
{
    BaseBase bb{};
    Base b1{bb};
    Derived d1{b1};
    return d1.test;
}

Here foo() returns 1, proving that the Base& copy constructor was called. This is what I would expect because the constructor was passed a Base instance.

If I change the #if 1 to #if 0, then instead of defining its own constructors, Derived gets the constructors defined in Base. I would therefore expect it to have the following constructors:

Derived()
Derived(const Base& other)
Derived(const BaseBase& other)

which are the same ones defined in-place in Derived, plus a default constructor. However now foo() returns 2 instead, proving that the BaseBase& constructor was called even though the code at the call-site has not changed and is still using a Base instance.

The code is also available here on Compiler Explorer.

My understanding of the overload resolution rules is that all else being equal, the "most specific" (IE furthest down the inheritence hierarchy) overload is selected. This is what happens when Derived has its own constructors but not when it imports constructors from Base. Why is the behaviour different? Why isn't the Base constructor used?

JacquesH
  • 161
  • 1
  • 7

0 Answers0