2009-07-13 9 views
11

Oto kod opisujący problem, z którym się zmagałam. Ostatnim problemem (o ile dotyczy to g ++) jest: "błąd:" Foo-T "nie został zadeklarowany w tym zakresie" podczas wykonywania procedury konstruktora Bar :: Bar (...). W przeciwnym razie problem, którego próbuję się nauczyć, polega na ustawianiu typów elementów bazowych na podstawie argumentów przekazywanych do konstruktora klasy pochodnej za pomocą szablonów. Jeśli byłby sposób na ustawienie typu członka klasy podstawowej (T Foo-T) po prostu przekazując argumenty do konstruktora klasy pochodnej, wolałbym to. W chwili obecnej nie widzę sposobu przeszłego używania zarówno argumentu szablonu, jak i zgodnego argumentu konstruktora klasy pochodnej, aby wykonać to zadanie. Czy potrafisz dostrzec cokolwiek w poniższym kodzie, że mogę robić lepiej, aby osiągnąć te same cele? Jestem raczej nowy w generycznym kodowaniu i szablonach.Inicjowanie podstawowych typów elementów szablonu w listach inicjalizujących klasy pochodnej

#include <iostream> 
typedef int a_arg_t; 
typedef double b_arg_t; 
typedef std::string foo_arg_t; 

class TypeA { 
public: 
    TypeA(); 
    TypeA (a_arg_t a) { 
     /* Do sosmething with the parameter passed in */ 
    } 

}; 

class TypeB { 
public: 
    TypeB(); 
    TypeB (b_arg_t b) { 
     /* typeB's constructor - do something here */ 
    } 

}; 

// The base-class with a member-type to be determined by the template argument 
template <class T> 
class Foo { 

public: 
    Foo (const foo_arg_t foo_arg) : _foo_arg(foo_arg)  // initialize something here 
    { 
     /* do something for foo */ 
    } 
    T Foo_T;  // either a TypeA or a TypeB - TBD 
    foo_arg_t _foo_arg; 
}; 

// the derived class that should set the basse-member type (T Foo_T) 
template <class T> 
class Bar : public Foo<T> { 
public: 
    Bar (const foo_arg_t bar_arg, const a_arg_t a_arg) 
    : Foo<T>(bar_arg) // base-class initializer 
    { 
     // the initialization of Foo_T has to be done outside the initializer list because it's not in scsope until here 
     Foo_T = TypeA(a_arg); // if an a_arg_t is passed in, then we set the Foo_T to TypeA, etc. 
    } 

    Bar (const foo_arg_t bar_arg, const b_arg_t b_arg) 
    : Foo<T>(bar_arg) 
    { 
     Foo_T = TypeB(b_arg); 
    } 

}; 

int main() { 

    b_arg_t b_arg; 
    a_arg_t a_arg; 
    foo_arg_t bar_arg; 

    Bar<TypeA> a (bar_arg, a_arg); // try creating the derived class using TypeA 
    Bar<TypeB> b (bar_arg, b_arg); // and another type for show 

return 0; 
} 
+0

Uwaga: domyślne konstruktory na obiektach typu A i TypeB potrzebują ciał, w przeciwnym razie wystąpią błędy łącza. – csj

Odpowiedz

14

Typ Foo_T nie zostanie znaleziony w klasie bazowej, jeśli jest używany w konstruktorze pochodnym (Bar).

Bar (const foo_arg_t bar_arg, const a_arg_t a_arg) 
: Foo<T>(bar_arg) // base-class initializer 
{ 
    Foo_T = TypeA(a_arg); TypeA, etc. // Won't compile, per the standard 
} 

Jest za standardzie C++, który mówi niewykwalifikowanych nazwy są generalnie nie-zależne i powinny być spojrzał w górę, gdy szablon jest w pełni zdefiniowany.

Ponieważ definicja szablonu klasy bazowej nie jest znana w tym czasie (mogą występować w pełni wyspecjalizowane instancje szablonu wciągniętego później w jednostce kompilacji), niewykwalifikowane nazwy nie są nigdy rozstrzygane na nazwy w zależnych klasach bazowych.

Jeśli potrzebujesz nazwy z klasy bazowej, gdy szablony są zaangażowane, musisz albo całkowicie je zakwalifikować, albo uczynić je niejawnie zależnymi w klasie pochodnej.

Foo<T>::Foo_T = TypeA(a_arg); // fully qualified will compile 

lub uzależnić

this->Foo_T = TypeA(a_arg); 

Ponieważ this sprawia, że ​​szablon zależne, rozwiązania typu jest przesunięty do „fazy 2” szablonu konkretyzacji (i wtedy klasa bazowa jest również w pełni znany)

Zauważ, że jeśli chce korzystać z funkcji z klasy bazowej, można mieć również dodać using oświadczenie ..

(wewnątrz paska())

some_foo_func(); // wouldn't work either 

    using Foo<T>::some_foo_func; 
    some_foo_func(); // would work however 
3

Niestety aby być pomocne, ale ja również nie widzę sposób wokół tego nie robiąc dokładnie to, co powiedział:

As of now I can't see a way past using both the template argument and a matching derived-class constructor argument to accomplish this task.

będzie prawdopodobnie mają specjalizować się trochę:

template<> 
Bar<TypeA>::Bar (const foo_arg_t bar_arg, const a_arg_t a_arg) 
: Foo<TypeA>(bar_arg) // base-class initializer 
{ 
    // the initialization of Foo_T has to be done outside the initializer list because it's not in scsope until here 
    Foo_T = TypeA(a_arg); // if an a_arg_t is passed in, then we set the Foo_T to TypeA, etc. 
} 

template< class T> 
Bar<T>::Bar (const foo_arg_t bar_arg, const a_arg_t a_arg) 
: Foo<T>(bar_arg) // base-class initializer 
{ 
    // Throw exception? 
} 

template<> 
Bar<TypeB>::Bar (const foo_arg_t bar_arg, const b_arg_t b_arg) 
: Foo<TypeB>(bar_arg) 
{ 
    Foo_T = TypeB(b_arg); 
} 

template< class T > 
Bar<T>::Bar (const foo_arg_t bar_arg, const b_arg_t b_arg) 
: Foo<T>(bar_arg) 
{ 
    // Throw exception ? 
} 

Niestety, nie mam obecnie dostępu do kompilatora, aby sprawdzić ten kod, więc bądź ostrożny.


W odpowiedzi na pytanie/komentarz. Mam następujący skompilować:

#include <iostream> 
typedef int a_arg_t; 
typedef double b_arg_t; 
typedef std::string foo_arg_t; 

class TypeA { 
public: 
    TypeA() {} 
    TypeA (a_arg_t a) {} 
}; 

class TypeB { 
public: 
    TypeB() {} 
    TypeB (b_arg_t b) {} 
}; 

template <class T> 
class Foo { 
public: 
    Foo (const foo_arg_t foo_arg) : _foo_arg(foo_arg) {} 
    T Foo_T;  // either a TypeA or a TypeB - TBD 
    foo_arg_t _foo_arg; 
}; 

// the derived class that should set the basse-member type (T Foo_T) 
template <class T> 
class Bar : public Foo<T> { 
public: 
    Bar (const foo_arg_t bar_arg, const a_arg_t a_arg) 
    : Foo<T>(bar_arg) // base-class initializer 
    { 
    Foo<T>::Foo_T = TypeA(a_arg); 
    } 

    Bar (const foo_arg_t bar_arg, const b_arg_t b_arg) 
    : Foo<T>(bar_arg) 
    { 
    Foo<T>::Foo_T = TypeB(b_arg); 
    } 
}; 

int main() { 
    b_arg_t b_arg; 
    a_arg_t a_arg; 
    foo_arg_t bar_arg; 

    Bar<TypeA> a (bar_arg, a_arg); // try creating the derived class using TypeA 
    Bar<TypeB> b (bar_arg, b_arg); // and another type for show 

    return 0; 
} 
+0

Czy jest jakiś pomysł na temat błędu kompilatora stwierdzającego, że Foo_T jest poza zakresem w konstruktorze Bar :: Bar (...)? Czy konstruktor klasy pochodnej nie ma dostępu do publicznych lub chronionych członków klasy bazowej? Zdaję sobie sprawę, że nie można uzyskać dostępu do podstawowych elementów danych na liście inicjalizacyjnej, ale zawsze miałem wrażenie, że konstruktor klasy pochodnej miał dostęp ... – Shamster

+0

Zmieniono moją odpowiedź, aby skompilować twój kod. Szczerze mówiąc ... Nie jestem pewien, dlaczego potrzebowałem jednoznacznie powiedzieć Foo :: Foo_T. –

+0

Wystąpiły błędy kopiowania i wklejania. Wypróbuj teraz kod. –

0

Kiedy po raz pierwszy spojrzał na kodzie, byłem absolutnie pewien, że trzeba by rozwiązać problem z częściowym specjalizujący. Rzeczywiście, może tak być nadal, jednak zmniejszyłem ilość kodu niezbędną do odtworzenia błędu i zauważyłem, że błąd występuje tylko podczas kompilacji z gcc (nie wiem, która wersja kompilatora działa na uniwersytecie), a podczas kompilacji z Visual Studio 2003 - wszystko jest szczęśliwe.

Poniższa replikuje kod błędu, ale niewielka, pozornie niewinna zmiana będzie zaskakująco wyeliminować go:

template <typename T> 
class ClassA 
{ 
public: 
    ClassA() {} 
    T vA; 
}; 


template<typename T> 
class ClassB : public ClassA<T> 
{ 
public: 
    ClassB() 
    { 
     vA = 6; 
    } 
}; 

int main() 
{ 
    ClassB<int> cb; 
} 

Teraz, jeśli usuniesz deklarację szablonu z ClassB, i to bezpośrednio dziedziczyć ClassA:

class ClassB : public ClassA<int> 
{ 
public: 
    ClassB() 
    { 
     vA = 6; 
    } 
}; 

a następnie zmienić deklarację cb dopasować

ClassB cb; 

Następnie błąd znika, mimo że wyraźnie nie ma nic innego co do zakresu vA (lub w twoim przypadku, Foo_T)

Spekuluję, że jest to błąd kompilatora i zastanawiam się, czy być może jest on całkowicie aktualny. Kompilator gcc nadal będzie występował.

+0

Właśnie czytałem aktualizację do innej odpowiedzi i próbowałem rozdzielczości zakresu zgodnie z zaleceniami. Lo a oto, to skompilowane. ClassA :: vA = 6; Jestem nieco zaskoczony, że gcc wymaga tej rozdzielczości, gdy wydaje mi się, że nawet w przypadku dziedziczenia szablonowego zakres powinien być jasny. Być może w standardzie jest jakiś dziwaczny szczegół, który by to wyjaśnił. – csj

Powiązane problemy