Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c++

c++17

c++20

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};
}
like image 954
Wang Avatar asked Apr 19 '20 20:04

Wang


2 Answers

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.

like image 145
cigien Avatar answered Oct 29 '22 01:10

cigien


One option is an explicit deduction guide (C++17):

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

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

template<typename ...As>
B_t( As...) -> B_t<As...>;

int main()
{
    base_t a{1, 2.0f};
    B_t b{1, 2.0f};

    B_t<int, float> bb=b;

    return 0;
}

More work will probably be needed to correctly deal with references, probably by throwing in a std::remove_reference_cv (remove_cvref_t for C++20), or a std::decay_t somewhere in there...

like image 30
Sam Varshavchik Avatar answered Oct 29 '22 00:10

Sam Varshavchik