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?

War es hilfreich?

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 (also foo<T>::foo oder TTP<T>::TTP, wobei TTP ein Vorlagenvorlagenparameter ist).
  • Wenn der letzte Teil mit dem Klassennamen übereinstimmt (also foo::foo oder T::T, wobei T 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ürde T::T (wobei T die Klasse foo bezeichnet) nicht funktionieren, da foo kein Mitglied namens T 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 den foo<T> der Basisklasse eingefügt und an bar vererbt wurde. Wir verweisen auf diesen Namen mit bar::foo und fügen dann den letzten Teil foo 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.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top