c ++ 0x geerbter Konstruktor in Vorlagen
-
29-10-2019 - |
Frage
Hier ist die Klasse foo:
template <typename T>
struct foo
{
foo()
{
t = nullptr;
}
foo(T* p, bool flag)
{
t = p;
}
private:
T* t;
};
Hier ist die Klassenleiste:
template <typename T>
struct bar: public foo<T>
{
using foo<T>::foo<T>;
};
Ist die Syntax für das Erben von Konstruktoren korrekt?Wenn ich "using foo :: foo;" verwendeDann stirbt der Compiler von Visual C ++ 2010. Wie erbe ich also Konstruktoren von Vorlagenklassen in VC ++ 2010?
Lösung
template <typename T>
struct bar: public foo<T>
{
using foo<T>::foo<T>;
};
Damit diese Analyse korrekt durchgeführt werden kann, müssen Sie vor dem template
foo<T>;
einfügen, um dem Compiler mitzuteilen, dass foo
als Vorlagenname anzusehen ist (er kann nicht in foo<T>
schauen, um sich selbst zu sagen, da T
unbekannt ist). Die Verwendung von ::template
ist in einer using-Deklaration jedoch nicht zulässig. Der Name bezieht sich auch nicht auf alle Konstruktoren von bar
: Stattdessen bezieht er sich auf eine bestimmte Spezialisierung von Konstruktorfunktionsvorlagen (T
ist das Vorlagenargument) eines solchen Konstruktors wie folgt:
template<typename T>
foo();
Darüber hinaus ist es für eine using-Deklaration nicht gültig, einen template-id
(wie foo<T>
) als Namen zu verwenden (was praktisch verbietet, auf die Funktionsvorlagenspezialisierung zu verweisen, wobei auch das Verbot der Namenskonvertierung von Funktionsvorlagenvorlagenspezialisierungen hinzugefügt wird ), selbst wenn Sie das Parsing-Problem mit ::template
beheben (falls dies möglich wäre), würden Sie an dieser Stelle immer noch einen Fehler machen.
Bei der Einführung geerbter Konstruktoren wurden spezielle Regeln hinzugefügt, mit denen ein Konstruktor mithilfe einer syntaktischen Regel referenziert werden kann: Wenn Sie eine qualifizierte ID (die im Grunde ein qualifizierter Name mit ...::...
ist) und die zuletzt qualifizierte vor den endgültigen Teilenamen haben Wenn Sie eine bestimmte Klasse haben, können Sie den / die Konstruktor (en) dieser Klasse auf zwei zusätzliche Arten bezeichnen:
- Wenn die Klasse mit einer Vorlagen-ID benannt wurde (ein Name des Formulars
foo<T>
) und der letzte Teil mit dem Vorlagennamen übereinstimmt (alsofoo<T>::foo
oderTTP<T>::TTP
, wobeiTTP
ein Vorlagenvorlagenparameter ist). - Wenn der letzte Teil mit dem Klassennamen übereinstimmt (also
foo::foo
oderT::T
, wobeiT
ein Vorlagenparameter ist).Diese beiden zusätzlichen Regeln sind nur in einer using-Deklaration aktiv. Und sie waren natürlich in C ++ 03 nicht vorhanden. Die andere Regel, die auch in C ++ 03 vorhanden war, lautet: Wenn der letzte Teil den Namen der injizierten Klasse benennt, bezieht sich dieser qualifizierte Name auch auf den Konstruktor:
-
foo::foo
würde dafür funktionieren. Aber mit dieser Regel allein würdeT::T
(wobeiT
die Klassefoo
bezeichnet) nicht funktionieren, dafoo
kein Mitglied namensT
hat.Daher können Sie mit den geltenden Sonderregeln schreiben
using foo<T>::foo; using bar::foo::foo; // valid too
Der zweite ist ebenfalls gültig:
foo
ist der injizierte Klassenname, der in denfoo<T>
der Basisklasse eingefügt und anbar
vererbt wurde. Wir verweisen auf diesen Namen mitbar::foo
und fügen dann den letzten Teilfoo
hinzu, der sich erneut auf den Namen der injizierten Klasse bezieht, um die Konstruktoren von `foo zu bezeichnen.Jetzt verstehen Sie, warum der ursprüngliche Name, den Sie versucht haben, auf eine Spezialisierung auf Konstruktorfunktionsvorlagen verweist (wenn dies zulässig wäre): Weil der
foo<T>::foo
-Teil alle Konstruktoren benennen würde und der folgende<T>
den dann herausfiltern würde Vorlage und übergeben Sie das Typargument.
-
Andere Tipps
Wenn Ihr Compiler das Erben von Konstruktoren noch nicht unterstützt, aber verschiedene Makros, verschiedene Vorlagen und rvalue-Referenzen sowie ein sehr praktisches type_trait unterstützt, finden Sie hier eine wirklich anständige Problemumgehung:
#include <type_traits>
#include <utility>
#include <ostream>
enum Color {Red, Blue};
#define USING(Derived, Base) \
template<typename ...Args, \
typename = typename std::enable_if \
< \
std::is_constructible<Base, Args...>::value \
>::type> \
Derived(Args &&...args) \
: Base(std::forward<Args>(args)...) { } \
template<typename Mixin>
class add_color
: public Mixin
{
Color color;
public:
USING(add_color, Mixin);
friend std::ostream& operator<<(std::ostream& os, const add_color& x)
{
switch (x.color)
{
case Red:
os << "Red";
break;
case Blue:
os << "Blue";
break;
}
os << ' ' << x.first << ' ' << x.second;
return os;
}
};
#include <string>
#include <iostream>
int main()
{
add_color<std::pair<std::string, int>> x1("five", 5);
std::cout << "x1 = " << x1 << '\n';
add_color<std::pair<std::string, int>> x3;
std::cout << "x3 = " << x3 << '\n';
add_color<std::pair<std::string, int>> x4 = x1;
std::cout << "x4 = " << x4 << '\n';
std::pair<std::string, int> p;
add_color<std::pair<std::string, int>> x5 = p;
std::cout << "x5 = " << x5 << '\n';
}
Wenn Sie is_constructible noch nicht haben, funktioniert die Grundidee ohne es, aber der "geerbte Konstruktor" wird übermäßig gierig sein.
Sie benötigen den zweiten Vorlagenparameter nicht;
template <typename T>
struct bar: public foo<T>
{
using foo<T>::foo;
};
sollte tun
edit Ich ziehe zurück, dass dies unter g ++ - 4.4.1 funktioniert. Dies sollte jedoch die richtige Syntax sein, wenn die Funktion verfügbar wird.
Die anderen Antworten haben bereits gut erklärt, wie das Erben von Konstruktoren in C ++ 0x funktioniert.Zum jetzigen Zeitpunkt hat jedoch kein Compiler den gesamten C ++ 0x-Funktionsumfang vollständig implementiert.Leider bedeutet dies, dass VC ++ 2010 das Erben von Konstruktoren noch nicht unterstützt.
Der C ++ 0x-Standard wurde noch nicht veröffentlicht.Der endgültige Entwurf des Standards wird irgendwann in fertiggestelltMärz , aber es wird noch einige Monate dauern, bis ISO es veröffentlicht.Während dieser Zeit führen Compiler-Writer Funktionen ein, damit sie nach Fertigstellung des Standards so C ++ 0x-konform wie möglich sind.
Ich glaube, die neueste Version von GCC unterstützt das Erben von Konstruktoren. Wenn Sie es jetzt ausprobieren müssen, können Sie es verwenden.Natürlich ist die C ++ 0x-Unterstützung experimentell und kann sich ändern, wenn Fehler gefunden werden usw.