2014-09-12 15 views
6

Rozważmy następujące klasy Szablon:C++ szablon z „const”

template <typename T> class Function { 
public: 
    virtual float eval(const T &x, const T &y) = 0; 
}; 

ponieważ funkcja „eval” nie powinien zmienić wartość z dwóch wejść „x” i „y”, kładę je jako " const ". Potem utworzyć następujące klasy pochodzący z funkcją

class Foo1 : public Function <float*> { 
public: 
    Foo1() : Function <float*>() {} 
    virtual float eval(const float* &x, const float* &y) { ... } 
}; 

Kiedy skompilować z g ++, pojawia się następujące ostrzeżenie:

hidden overloaded virtual function 'Function<float *>::eval' declared here: type mismatch at 1st parameter 
     ('float *const &' vs 'const float *&') 
     virtual float eval(const T &x, const T &y) = 0; 

I nie mogę instancję klasy foo1. Kompilator mówi, że funkcja "eval" nie jest zaimplementowana. Aby kompilator szczęśliwy, klasa pochodzić musi być następująca:

class Foo2 : public Function <float*> { 
public: 
    Foo2() : Function <float*>() {} 
    virtual float eval(float* const &x, float* const &y) { ... } 
}; 

funkcja foo2 :: eval wykorzystuje dwa parametry typu „pływaka * const” zamiast „const pływaka *”. Innymi słowy, Foo2 :: eval może modyfikować zawartość tablic "x" i "y". Nie tego chcę.

Próbowałem zmienić szablon klasy „funkcja” w następujący sposób:

virtual float eval(T const &x, T const &y) = 0; 

Ale klasa foo1 nadal nie działa, klasa foo2 działa podobnie jak w poprzednim przypadku.

  1. Wygląda więc na to, że albo 'const T & x' lub 'T const & x' w klasie szablonu zakłada 'pływaka * const & x' w klasie pochodnej. Czy to jest poprawne?
  2. Jeśli chcę "const float * & x '(lub" flo float * x') w klasie pochodnej, jaka powinna być moja klasa szablonu Funkcja?

Dziękuję.

Odpowiedz

1

Wydaje się, że zarówno const T &x lub T const &x w klasie szablonu zakłada float* const &x w klasie pochodnej. Czy to jest poprawne?

Tak, zgadza się. Sposób myślenia o tym, że T jest zawsze const; w twoim przypadku T jest wskaźnikiem.

Jeśli chcę const float* &x (lub const float* x) w klasie pochodnej, jaki powinien być mój szablon klasy funkcji?

Brzmi jak powinno być class Foo2 : public Function <const float*>.

0

Wyjściem jest częściowo specjalizują Function dla typów wskaźnikowych jak użyć innego podpisu dla eval:

template <typename T> class Function<T*> { 
    public: 
     virtual float eval(const T* x, const T* y) = 0; 
}; 

Alternatywnie, można czerpać Foo2 z Function<const float*>.

8

Obserwowane zachowanie jest w 100% poprawne, jeśli chodzi o standard. To klasyczny przykład "stałego wskaźnika" a "wskaźnika do stałego".

Twój podstawowy szablon deklaruje, że ma „odniesienie do T przez które nie można zmodyfikować obiekt T jest mowa” (składnia jest const T &, odpowiednik T const &).

Następnie należy utworzyć instancję szablonu o typie float*, tj. "Wskaźnik do float." Z tego wynika, że ​​typ parametru funkcji po substytucji parametru szablonu jest rzeczywiście "odniesieniem do float *, przez które nie może on modyfikować, rozpoczynając odniesienie do float *". Nie ma sposobu na przeszmuglowanie "nie można zmodyfikować , do której odnosi się bezpośrednio" punkt ".

Widzę dwie opcje. Jeden, jeśli ten rodzaj użytkowania jest jedynym wykorzystanie T w Function, wystarczy użyć const float * jako argument szablonu, ponieważ jest to T naprawdę chcesz:

class Foo1 : public Function <const float*> { 
public: 
    Foo1() : Function <const float*>() {} 
    virtual float eval(const float* const &x, const float* const &y) { ... } 
    // notice two `const` keywords above: one for the pointed object, one for the reference 
}; 

Jeśli nie jest to opcja dla Ciebie (czyli jeśli potrzebujesz float * gdzieś wewnątrz Function i const float* w innym miejscu), będziesz musiał użyć pewnych cech i zmodyfikować parametry eval. Coś takiego:

template <class T> 
struct unmodifiable { 
    typedef const T &type; 
}; 

template <class T> 
struct unmodifiable<T*> { 
    typedef const T* const &type; // if you want to keep the reference 
    // or just: 
    // typedef const T *type; // if you don't want to bother with the reference for pointer types 
}; 

template <typename T> class Function { 
public: 
    virtual float eval(typename unmodifiable<T>::type x, typename unmodifiable<T>::type y) = 0; 
}; 

class Foo1 : public Function <float*> { 
public: 
    Foo1() : Function <float*>() {} 
    virtual float eval(unmodifiable<float*>::type x, unmodifiable<float*>::type y) { ... } 
    // or just spell it out exactly, based on the variant of `unmodifiable` you've chosen, e.g.: 
    // virtual float eval (const float *x, const float *y) { ... } 
};