Home > Mobile >  deducing variadic inheritance type from constructor
deducing variadic inheritance type from constructor

Time:08-20

I have a struct that can have a variadic number of members by inheriting from them:

struct A{ int a; };
struct B{ float b; };
struct C{ const char* c; };


template<typename ... Ts>
struct collection : Ts... {

};

int main() {
    collection<A, B, C> n;
    n.a = 5;
    n.b = 0.5f;
    n.c = "123";
}

now I want to use the initializer-list constructor:

auto n = collection<A, B, C>{ 5, 0.5f, "123" };

but I have to name the templates A, B, C. The compiler should figure this out. I want to achieve this:

auto n = collection{ 5, 0.5f, "123" };

but this doesn't work

main.cpp(8,1): error C3770: 'int': is not a valid base class
main.cpp(12): message : see reference to class template instantiation 'collection<int,float,const char *>' being compiled
main.cpp(8,1): error C3770: 'float': is not a valid base class
main.cpp(8,1): error C3770: 'const char *': is not a valid base class
main.cpp(12,38): error C2440: '<function-style-cast>': cannot convert from 'initializer list' to 'collection<int,float,const char *>'
main.cpp(12,21): message : Invalid aggregate initialization

Visual Studio 2022 17.3.0, /std:c latest, Debug x64


Is there a (clean) way to deduce the right type? It should be possible with the right constructor, since all underlying types are uniquely assignable.

CodePudding user response:

You can do this using a user defined deduction guide and a couple "meta functions" to map the parameter type to the desired class type. First the meta functions get declared like

A type_map(int);
B type_map(float);
C type_map(const char*);

and then we can create a deduction guide in the form of

template<typename... Ts>
collection(Ts... ts) -> collection<decltype(type_map(ts))...>;

and now

auto n = collection{ 5, 0.5f, "123" };

will have n being a collection<A, B, C> as seen in this live exmaple


A "meta function" is the term I use for a function that doesn't actually do anything, it is just used to map one type into another. The are only ever called in an unevaluated context like in decltype in the above example.

  • Related