简体   繁体   中英

How to convert between different instantiations of the same variadic template?

Assume we have a data structure Foo that maintains a set of elements. It should be possible to associate attributes with the elements as needed. The attributes should be stored in a separate vector each. We implement this by means of variadic templates:

#include <vector>

template <typename ...Attrs>
struct Foo : public Attrs... {
  Foo(int n = 0) {
    using PackExpansionT = int[];
    PackExpansionT{0, (Attrs::values.resize(n), 0)...};
  }
};

struct AttrA { std::vector<int>    values; };
struct AttrB { std::vector<float>  values; };
struct AttrC { std::vector<double> values; };

int main() {
  Foo<AttrA, AttrB> foo; // Maintains set of elements with two attributes each.
};

Now, I want a conversion operator with the following semantics:

  Foo<AttrB, AttrC> bar = foo; // bar.AttrB::values should be a copy of foo.AttrB::values.

This is only an example. In general, the conversion operator should be able to convert a Foo with arbitrary attributes into another Foo with arbitrary attributes. Attributes associated with both Foo s should be copied. Attributes not associated with both can be left defaulted. However, I have no idea how to implement it.

  template <typename ...OthersAttrs>
  operator Foo<OthersAttrs...>() const {
    // ...?
  }

We can just make a bunch of independent decisions. First, let's add a constructor so that we can construct Foo from its attribute constituents:

Foo(Attrs const&... attrs)
: Attrs(attrs)...
{ }

Next, for each attribute in Others , we will either downcast this to the appropriate type if possible or return a default-constructed one otherwise:

template <typename... Others>
operator Foo<Others...>() const {
    return {get_attr<Others>(this)...};
}

where:

template <class T>
T const& get_attr(T const* v) const {
    return *v;
}

template <class T>
T get_attr(...) const {
    return T{};
}

An outline I can think of:

template <typename ...OthersAttrs>
operator Foo<OthersAttrs...>() const
{
    Foo<OthersAttrs...> rv(GetNSomehow());
    (int[]){(CopyAttr<Attrs>(&rv), 0)...};
    return rv;
}

template<typename Attr>
void CopyAttr(Attr *rv) const // Overload A
{
    rv->values = ((const Attr*)this)->values;
}

template<typename Attr>
void CopyAttr(...) const // Overload B
{

}

The trick here is to go attribute by attribute.
If rv has the attribute the first overload will be chosen and it will be copied.
Otherwise the second overload would be chosen that will do nothing.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM