Search code examples
c++c++17c++20c++23

how to make argument deduction work for derived class which using base class constructor?


When derived class using base class constructor the deduction seems always fail. However, when the base class have lots of constructors it is very clumsy to re-define all the constructors. It is also a pain when base class are quickly evolved with new constructors. The old question was asked more than 2 years ago, so I wonder: is there any work around for this in 2020 when c++17 and c++2a are available?

template<typename ...As>
class base_t
{
public:
    base_t(As... args){}
};

template<typename ...As>
class A_t: public base_t<As...>
{
public:
    A_t(As... args): base_t<As...>{args...} {};
};

template<typename ...As>
class B_t: public base_t<As...>
{
    using base_t<As...>::base_t;
};

int main()
{
    base_t a{1, 2.0f};
    A_t{1, 2.0f};
    B_t{1, 2.0f}; //fails unless explicitly specialize the template
    return 0;
}

updates according to @Sam and @Barry:

The deduction guide is very helpful. However, for a little be more complicate situation, it still runs out of control:

template <typename A>
struct D_t {
    A x;
    D_t(A x) :x{x} {}
};
template<typename A, typename B>
class base2_t
{
public:
    base2_t(A a, B b){std::cout << "1\n";}
    base2_t(A a, D_t<B> c, int x){std::cout << "2\n";}
    base2_t(A a, B b, int x){std::cout << "3\n";}
    base2_t(A a, B b, int x, float y){std::cout << "4\n";}
    explicit base2_t(A(*fp)(B)){std::cout << "5\n";}
    // if we have lots of similar things like above
    // we will quickly end up write lots of different
    // guides.
};
template<typename A, typename B>
class C_t: public base2_t<A, B>
{
    using base2_t<A, B>::base2_t;
};
template<typename A, typename B, typename ...As>
C_t(A, B, As...)->C_t<A, B>;
template<typename A, typename B>
C_t(A(*)(B))->C_t<A, B>;
float func1(int x)
{
    return x;
}
int main()
{
    C_t{1, 2.0f, 3};
    base2_t{1, D_t{2.0f}, 3};
    C_t{1, D_t{2.0f}, 3}; // this is wrong, we have to deal with it by checking types and write different guides.
    base2_t{&func1};
    C_t{&func1};
}

till 2023, the proposal is accepted in c++23 P2582R1 But neither gcc nor llvm has implemented it yet. But there is a hope that in the middle of this year this issue will finally get fixed.


Solution

  • Being able to inherit deduction guides from base classes was proposed for c++20. However, this feature didn't make it in, as the last line says:

    The wording for CTAD from inherited constructors was not finalized in time for the C++20 committee draft, and will be published in a separate wording paper at a later point in time.

    So as of now, you will need to provide deduction guides for the derived class explicitly (or define the constructor as you did for A_t). Hopefully, this will be fixed in c++23.