Verallgemeinerte Mixins
-
27-10-2019 - |
Frage
Ich habe einen Code geschrieben, in dem ich eine Klasse habe, die Mixins als variadische Vorlagenparameter akzeptieren kann. Ich brauche jedoch auch die Mixins, um über das CRTP -Idiom auf die Basisklasse zugreifen zu können. Hier ist ein minimales Beispiel, das nicht genau das tun kann, was ich will:
template <template <class> class... Mixins>
class Foo : Mixins<Foo<Mixins...>>... {};
Ein Mixin, an den ich möglicherweise passieren könnte Foo
wird im Allgemeinen mehrere Vorlagenparameter wie SO haben:
template <class Derived, class Type1, class Type2>
class Bar
{
Derived& operator()()
{
return static_cast<Derived&>(*this);
}
};
Wie kann ich mich ändern Foo
Damit ich es von einer Reihe von Basisklassen erben kann, wo ich die von jeder Basisklasse akzeptierten Vorlagenparameter steuere? Wenn ich Hand habe Foo
Eine Liste von Vorlagen-Template-Parametern sowie eine Liste von Argumenten, die an sie weitergegeben werden sollen, sehe ich nicht, wie ich jeden Vorlagen-Template-Parameter mit seinen Argumenten assoziieren kann. Bisher dachte ich an so etwas, aber ich weiß nicht, wie ich vorgehen würde.
template <template <class...> class T,
template <class...> class... Ts>
class Foo : /* How do I retrieve the arguments? */
Lösung
Ich bin mir nicht ganz sicher, ob ich das Problem verstanden habe. Bitte lassen Sie mich es umformeln, damit wir auf dem richtigen Fuß beginnen können.
Sie müssen den abgeleiteten Typ in den Basisklassen in einem typischen CRTP -Anwendungsfall einfädeln, während gleichzeitig einen anderen Vorlagenparameter an die verschiedenen Basisklassen übergeben.
Das heißt, eine typische Basisklasse wird:
template <typename Derived, typename X, typename Y>
struct SomeBase {
};
Und Sie möchten Ihren Typ erstellen müssen, damit Sie die steuern können X
und Y
und gleichzeitig die komplette übergeben Derived
Klasse.
Ich denke, ich würde das verwenden apply
Trick, um die Basisklasse im laufenden Fliegen aus einem Adapter zu generieren Derived
Klasse.
template <typename Derived, typename X, typename Y>
struct SomeBase {};
template <typename X, typename Y>
struct SomeBaseFactory {
template <typename Derived>
struct apply { typedef SomeBase<Derived, X, Y> type; };
};
// Generic application
template <typename Fac, typename Derived>
struct apply {
typedef typename Fac::template apply<Derived>::type type;
};
Dann erstellen Sie den Typ als:
typedef MyFoo< SomeBaseFactory<int, float> > SuperFoo;
Wo Foo
ist definiert als:
template <typename... Args>
struct Foo: apply<Args, Foo<Args...>>::type... {
};
Und nur weil es eine Weile her ist, seit ich so tief in Vorlagen gestapft habe, Ich habe es überprüft, es funktionierte.
Natürlich die Factory
selbst ist für einen bestimmten Typ nicht wirklich spezifisch, sodass wir den Wrapper -Ansatz, den Sie experimentiert hatten, wiederverwenden können:
template <template <typename...> class M, typename... Args>
struct Factory {
template <typename Derived>
struct apply { typedef M<Derived, Args...> type; };
};
Und ja, Es funktioniert auch.
Andere Tipps
Wenn ich Ihre Frage korrekt verstehe, sollten Sie Vorlagen -Aliase erstellen, die jedes Mixin auf einen einzelnen Vorlageparameter reduzieren.
template <typename Derived>
using BarIntFloat = Bar<Derived, Int, Float>;
template <typename Derived>
using BazQux = Baz<Derived, Qux>;
typedef Foo<BarIntFloat, BazQux> MyFoo;
Hier ist eine Lösung, die ich mir ausgedacht habe. Es mag eine elegantere Möglichkeit geben, dies zu tun, aber ich könnte mir keine vorstellen. Eine Einschränkung ist, dass alle verwendeten Mixins zuerst in der verschachtelt werden müssen wrapper
Struktur zusammen mit ihren jeweiligen Argumenten.
template <template <class...> class Mixin, class... Args>
struct wrapper
{
typedef Mixin<Args...> type;
};
template <class... Args>
struct test
{
};
template <class Arg, class... Args>
struct test<Arg, Args...> : Arg::type, test<Args...>
{
};
template <class T>
class mixin1 {};
template <class T1, class T2>
class mixin2 {};
template <class T1, class T2, class T3>
class mixin3 {};
int main()
{
test<wrapper<mixin1, int>, wrapper<mixin2, int, float>> foo;
return 0;
}
@void-pointer
Dies ist eine grundlegende Auslassung von variadischen Vorlagen. Der Benutzer kann nicht von t ... oder i-te Wert aus Werten erhalten ...
Hier ist eine Verbindung Von Going Native 2012 Lecture von Andrei Alexandrescu:
template <typename... Ts>
void fun(const Ts&... vs) {}
• TS ist kein Typ; vs ist kein Wert!
typedef Ts MyList; // error!
Ts var; // error!
auto copy = vs; // error!
TS/VS sollte also eine Art Tupel sein.