2015-04-13 19 views
16

Mam trzy różne kompilatory, których używam do kompilacji tego kodu. Jeden z nich (ten, któremu ufam najmniej) ostrzega, że ​​funkcja w Derived ukrywa funkcję w Base. Pozostałe kompilatory (jedna to Visual C++) nie ostrzega. Visual C++ nawet nie ostrzega o tym, jeśli włączę/Wall lub/W4.Czy funkcja zastępuje funkcję podstawową?

Mam wrażenie, że jest to błąd w kompilatorze, który ostrzega, ponieważ kompiluje kod. Jeśli naprawdę nie zastąpiłoby funkcji podstawowej, powinien dać błąd, gdy utworzę instancję wyprowadzonego szablonu.

Czy ktoś może potwierdzić, jak to powinno się zachować?

struct Base 
{ 
    virtual void Func(float f) = 0; 
}; 

template <typename T> 
struct Derived : Base 
{ 
    virtual void Func(T f){} 
}; 

int main() 
{ 
    Derived<float> d; 
    d.Func(0); 
    return 0; 
} 

Kiedy Derived jest tworzony z float uzyskać nieoczekiwane ostrzeżenie. Po utworzeniu Derived z int pojawia się błąd zgodnie z oczekiwaniami.

+3

Co jest nie tak z ostrzeżeniem na temat poprawnego kodu, który może nie być zgodny z oczekiwaniami? Na przykład 'if (b = true)' zwykle powoduje ostrzeżenie. – chris

+1

Nie sądzę, że powinien generować ostrzeżenie w ogóle - nie ma nic złego w kodzie. Nigdy nie widziałem tego typu rzeczy (klasa pochodna korzystająca z funkcji szablonów), spowodowało, że pomyślałem ciężko (ouch) – pm100

+0

Mam wrażenie, że faktycznie ukrywa ona funkcję w Base, ponieważ jeśli spróbujesz to zrobić: Base * b = new Pochodzi (); w twoim głównym przypadku wystąpi błąd kompilacji: błąd C2259: "Pochodny ": nie można utworzyć instancji klasy abstrakcyjnej 1> z powodu następujących elementów: 1> 'void Base :: Func (float)': jest abstrakcyjne to wygląda dla mnie podejrzanie. – RedOctober

Odpowiedz

7

To rzeczywiście jest przesłonięte. Można łatwo przekonać się w C++ 11 stosując override słowa kluczowego, który nie pozwoli, aby skompilować kod, jeśli funkcja nie jest przesłonięta:

struct Base 
{ 
    virtual void Func(float f) = 0; 
    virtual ~Base() = default; // to silence warnings 
}; 

template <typename T> 
struct Derived : Base 
{ 
    void Func(T f) override {} // will fail to compile if not overriding 
}; 

int main() 
{ 
    Derived<float> d; 
    d.Func(0); 
    return 0; 
} 

żywo przykładowy here.

Należy zauważyć, że w pre C++ 11, można przypadkowo ukryć funkcję virtual bazowej poprzez zmianę jego podpisu w klasie pochodnej, więc nawet jeśli zaznaczyć funkcję pochodzącą virtual kod nadal kompiluje, ale nie jest już polimorficzny , zobacz taki przykład: here. Niestety g ++ nie dostarcza żadnych ostrzeżeń, nawet z -Wall -Wextra. Dlatego override jest znacznie bezpieczniejszym sposobem na faktyczne wymuszanie prawdziwego nadpisania w czasie kompilacji.

4

Nie sądzę, że powinieneś otrzymać ostrzeżenie.
To jest taka sama, jak:

struct Derived : Base 
{ 
    virtual void Func(float f) { }; 
}; 

Gdy parametr szablonu jest float.

Nie ma ukrywania, a jedynie implementacja abstrakcyjnej funkcji.

3

W tym kontekście ostrzeżenie, że funkcja jest ukryta, informuje nas, że funkcja składowa w klasie pochodnej ma tę samą nazwę, ale inną sygnaturę niż funkcja w klasie bazowej. Rozważmy:

struct Base 
{ 
    void foo(int) {} 
    void bar(int) {} 
}; 

struct Derived: Base 
{ 
    void bar(int, int) {} 
}; 

int main() 
{ 
    Derived d; 
    d.foo(1); 
    d.bar(1); // will not compile: Base::bar is hidden by Derived::bar 
} 

W tym przykładzie, intencją mogło dodać dodatkową funkcję o nazwie „bar”, ale wynik jest, że kompilator zatrzymuje poszukuje nowych zakresów z nazwą funkcjonuje bar raz znajdzie zakresu z funkcja o nazwie bar. Zatem bar (int) jest ukryty przez pasek (int, int) (lub dowolny inny pasek z niezgodną sygnaturą). (Lub w przypadku nie wirtualnym, nawet jeśli funkcje pasują do siebie).

W tym kodzie Graznaraka, Baza :: Func jest ukryta w każdej sytuacji, w której Derived jest tworzone dla dowolnej wartości T, która nie jest zmienna (lub zmienna) const).

Graznarak pyta o prawidłowe zachowanie. Poprawny przed wygenerowanym kodem nie ma znaczenia Wyprowadzenie :: Func() zostaje wywołane.

Ale to pozostawia pytanie: Czy ostrzeżenie jest właściwe. Standard nie ma odpowiedzi. Nigdy nie wyraża opinii, czy należy generować ostrzeżenie. To, czy ostrzec przed konkretnymi problemami, czy nie, jest zawsze subiektywne, a kompilatorzy mogą się wyróżnić, wykazując dobrą ocenę w tym zakresie.

Czy Twój kompilator powinien ostrzegać przed tą sytuacją? Można sobie wyobrazić, że napisany kod robi to, co prawdopodobnie jest zamierzone. Ale istnienie szablonu sugeruje, że będzie on tworzony na więcej niż jednym typie (w przeciwnym razie, dlaczego tworzy szablon) i dla każdego innego typu, będzie się ukrywać. Można więc argumentować, że ostrzeżenie powinno zostać udzielone wraz z utworzeniem wyprowadzonego szablonu. Można jednak argumentować, że ostrzeżenie nie powinno nastąpić, dopóki nie zostanie określona instancja typu bez funkcji float.

Argumentacja za pierwszym jest taka, że ​​ostrzeżenie nastąpi wcześniej i prawdopodobnie zostanie wykryte przez programistę, który pisze problematyczny kod. Argumentowanie za późniejsze jest takie, że dopóki nie zostanie utworzony typ nie-float, nie ma żadnej podejrzanej sytuacji, o której należy ostrzec.

Powiązane problemy