Funktion wird in Basisklasse nicht gefunden



  • Hi, warum zum Henker findet der Compiler die Length Funktion aus der Basisklasse "ClassA" im "operator=" nicht? Erst wenn man der Funktion die Basisklasse davorschreibt (ClassA<T>::Length(N)) kann der Compiler diese zuordnen. Dabei hängt die Funktion Length nicht mal vom Templateargument ab. Das kann doch nicht wahr sein. Langsam muss man C++ wirklich als kaputt bezeichnen. Eine Schande was aus dieser Sprache geworden ist. Alles mit unnützen (und halbgaren dazu) Features aufblähen, aber absolute Basics gehen nicht ohne völlig unnötigen code bload...

    template <class T>
    class ClassA
    {
    public:
    	ClassA() : _length(0) { };
    	ClassA(size_t length) : _length(length) { };
    
    	size_t Length() const { return _length; };
    	void Length(size_t length) { _length = length; };
    
    private:
    	size_t _length;
    
    };
    
    template <class T>
    class ClassB : public ClassA<T>
    {
    public:
    	ClassB() { };
    
    	template <size_t N>
    	ClassB(const T (&value)[N]) :
    		ClassA<T>(N)
    	{
    	}
    
    	template <size_t N>
    	ClassB<T>& operator=(const T (&value)[N])
    	{
    		ClassA<T>::Length(N);
    		return *this;
    	}
    
    };
    
    int main()
    {
    	ClassB<char> test = "Test";
        return 0;
    }
    


  • Welchen Compiler nutzt du? Denn beim gcc geht es: Ideone-Code

    Oder hast du #include <cstdlib> (oder #include <cstddef>) vergessen (wegen size_t)?


  • Administrator

    Also mit GCC 7.3 kommt der Fehler auch. Fehler lautet:

    main.cpp:53:9: error: there are no arguments to ‘Length’ that depend on a template parameter, so a declaration of ‘Length’ must be available [-fpermissive]
             Length(10);
             ^~~~~~
    main.cpp:53:9: note: (if you use ‘-fpermissive’, G++ will accept your code, but allowing the use of an undeclared name is deprecated)
    

    Hab dazu das hier gefunden, bin es aber selber noch am lesen und verstehen: https://stackoverflow.com/a/4643295

    Ein möglicher Workaround ist jedenfalls mit this-> zu qualifizieren. Wäre immerhin etwas kürzer.



  • Wenn deine Basisklasse eine Template-Klasse ist musst du Member immer mit this oder dem Scope-Operator qualifizieren. Das ist so aber ich weiß nicht warum. Jedenfalls find ich das auch nervig.


  • |  Mod

    @Enumerator

    ClassA<T> hängt ganz offensichtlich vom Templateparameter T ab, und wird deshalb beim unqualifizierten Lookup nicht durchsucht (2-phase lookup). Um ein Durchsuchen per ADL zu erreichen, muss der Ausdruck Length so verändert werden, dass klar ist, dass dieser Ausdruck ebenfalls vom Templateargument abhängt (typischerweise durch this->Length), oder aber man arbeitet direkt mit einem qualifizierten Bezeichner. Der Klassenbezeichner muss nicht unbedingt die Basisklasse bezeichnen, es genügt, wenn klar ist, dass ein (ggf. geerbetes) Klassenmember gemeint ist (also z.B. ClassA<T>::Length , ClassB<T>::Length, ClassB::Length in diesem Fall).

    @Enumerator sagte in Funktion wird in Basisklasse nicht gefunden:

    Das kann doch nicht wahr sein. Langsam muss man C++ wirklich als kaputt bezeichnen. Eine Schande was aus dieser Sprache geworden ist. Alles mit unnützen (und halbgaren dazu) Features aufblähen, aber absolute Basics gehen nicht ohne völlig unnötigen code bload...

    Das beschriebene Verhalten besteht seit 20 Jahren so (-die Anzahl der Jahre, bis alle Compiler konform waren). Also genug Zeit, sich dieses Wissen anzueignen.



  • @Enumerator
    Der Grund warum Length ohne Weiteres nicht gefunden wird, ist, dass es alle möglichen Spezialisierungen von ClassA geben könnte, die alle möglichen Funktionen definieren. Spezialisierungen müssen ja nicht dem Haupt-Template ähnlich sein. Und vor allem werden Spezialisierungen auch manchmal von anderen Entwicklern geschrieben als das Haupt-Template.

    Die Regel existiert nun um Überaschungen und Fehler zu vermeiden.

    Angenommen du verwendest in ClassB jetzt eine bestimmte freie Funktion. Und angenommen in irgend einer Spazialisierung von ClassA<T> wird dann eine Memberfunktion des selben Namens definiert. Dann würde ohne diese Regel je nach Template-Parameter die freie Funktion oder eben die Memberfunktion von ClassA<T> verwendet. Weiters würde, falls die Funktion gar nicht existiert, die Fehlermeldung so lange verzögert bis wirklich das Template ClassB mit einem konkreten Typ instanziert wird.

    Beides ist nicht wünschenswert, und die Regel verhindert beides. Und wie @camper schon geschrieben hat, gibt es diese Regel seit C++98. Bloss dass bestimmte Compiler recht lange gebraucht haben um sie umzusetzen - Visual C++ z.B. war da IIRC recht spät dran.



  • @Dravere sagte in Funktion wird in Basisklasse nicht gefunden:

    Ein möglicher Workaround ist jedenfalls mit this-> zu qualifizieren. Wäre immerhin etwas kürzer.

    Ich würde this->Foo in den meisten Fällen vorziehen, auf Grund der Unterschiede zu ClassA<T>::Foo:

    1. Bei this->Foo bleibt ein evtl. virtueller Funktionsaufruf virtuell
    2. Bei this->Foo wird Foo nicht auf ClassA<T>::Foo festgenagelt, was einen Unterschied machen kann sobald man eine weitere Basisklasse zwischen ClassB und ClassA einfügt

    In beiden Fällen ist IMO öfter das Verhalten von this->Foo erwünscht als das Verhalten von ClassA<T>::Foo.