Templates, Funktionsüberladung, Fehler weil falsche Funktion aufgerufen wird


  • Administrator

    #include <iostream>
    
    #include <cassert>
    
    struct Control
    {
    	char const* get_value() const
    	{
    		return "{value of control}";
    	}
    };
    
    template<typename ControlT>
    void foo(ControlT const& control)
    {
    	std::cout << "Ctrl value: " << control.get_value() << std::endl;
    }
    
    template<typename ControlT>
    void foo(ControlT const* control)
    {
    	std::cout << "Validate control pointer." << std::endl;
    	assert(control);
    
    	foo(*control);
    }
    
    int main()
    {
    	Control ctrl;
    	Control* p = &ctrl;
    
    	foo(p); // Error, weil foo(ControlT const& control) aufgerufen wird.
    	// ControlT gesetzt auf Control*,
    	// Statt foo(ControlT const* control) und ControlT gesetzt auf Control.
    
    	return 0;
    }
    

    Wieso ruft der Kompiler nicht die Funktion auf, welche den Zeiger erwartet, vor allem wenn er dann entdeckt, dass es bei der ersten Funktion einen Kompilerfehler gibt.

    Wie kann ich den Kompiler dazu bringen, dass er die richtig Funktion verwendet? Was übersehe ich?

    Grüssli



  • foo(static_cast<Control const*>(p)); 
    //anstatt
    foo(p);
    

    Hatte das Problem auch schon mal. Keine Ahnung, wieso die Compiler das bei const & und const * so komisch auflösen.
    Alternativ kannste auch die constness bei der Pointer-Version wegmachen.


  • Administrator

    Tachyon schrieb:

    foo(static_cast<Control const*>(p)); 
    //anstatt
    foo(p);
    

    Dann aber lieber gleich:

    foo<Control>(p);
    

    Aber ich möchte ja eigentlich, dass der Kompiler das selber erkennt und ich es nicht mehr extra hinschreiben muss 😉

    Tachyon schrieb:

    Alternativ kannste auch die constness bei der Pointer-Version wegmachen.

    Nein, kann ich nicht. Es gibt auch Zeiger, welche auf konstante Objekte zeigen. Die sollen auch übergeben werden können 😉

    Grüssli



  • Dravere schrieb:

    Wieso ruft der Kompiler nicht die Funktion auf, welche den Zeiger erwartet, vor allem wenn er dann entdeckt, dass es bei der ersten Funktion einen Kompilerfehler gibt.

    Es gibt SFINAE, aber nicht CFINAE 😉

    Dravere schrieb:

    Wie kann ich den Kompiler dazu bringen, dass er die richtig Funktion verwendet?

    template <typename T>
      struct RemovePointer
    { typedef T type; };
    template <typename T>
      struct RemovePointer <T*>
    { typedef T type; };
    
    template <typename ControlT>
      void fooDecorator (const ControlT& control)
    { foo <typename RemovePointer <ControlT>::type> (control); }
    

  • Administrator

    @audacia,
    CFINAE? Was meinst du mit dem C? 🙂

    Zu deiner Lösung:
    Naja, aber da kann ich den zusätzlichen Code bei Zeigern gleich vergessen. Dann wäre diese Lösung noch besser:

    #include <boost/type_traits/is_pointer.hpp>
    
    #include <iostream>
    
    #include <cassert>
    
    template<int>
    struct Int2Type { };
    
    template<bool SELECT_IF, typename IfT, typename ElseT>
    struct Select
    {
    	typedef IfT Type;
    };
    
    template<typename IfT, typename ElseT>
    struct Select<false, IfT, ElseT>
    {
    	typedef ElseT Type;
    };
    
    template<typename ControlT>
    void foo_helper(ControlT const& control, Int2Type<true>)
    {
    	std::cout << "Ctrl value: " << control.get_value() << std::endl;
    }
    
    template<typename ControlT>
    void foo_helper(ControlT const* control, Int2Type<false>)
    {
    	std::cout << "Validate control pointer." << std::endl;
    	assert(control);
    
    	foo_helper(*control, Int2Type<true>());
    }
    
    template<typename ControlT>
    void foo(ControlT const& control)
    {
    	foo_helper(
    		control,
    		Select
    		<
    			boost::is_pointer<ControlT>::value,
    			Int2Type<false>,
    			Int2Type<true>
    		>::Type());
    };
    
    struct Control
    {
    	char const* get_value() const
    	{
    		return "{value of control}";
    	}
    };
    
    int main()
    {
    	Control ctrl;
    	Control* p = &ctrl;
    
    	foo(p);
    
    	return 0;
    }
    

    Grüssli 😉



  • Dravere schrieb:

    @audacia,
    CFINAE? Was meinst du mit dem C? 🙂

    Compilation.

    Dravere schrieb:

    Zu deiner Lösung:
    Naja, aber da kann ich den zusätzlichen Code bei Zeigern gleich vergessen.

    ?
    Was tut mein Code denn nicht, was du erwartest?


  • Administrator

    audacia schrieb:

    Dravere schrieb:

    @audacia,
    CFINAE? Was meinst du mit dem C? 🙂

    Compilation.

    Wieso hat das was mit Compilation und nicht mit Substitution zu tun? Ein Zeiger kann nicht per . (dot) verwendet werden. Das sollte doch auch bei der Substitution erkannt werden. Zudem, wieso wird die Funktion mit der Referenz gewählt, statt der Funktion mit dem Zeiger?

    audacia schrieb:

    Was tut mein Code denn nicht, was du erwartest?

    Mein Code hat bei einem Zeiger ein assert und ein std::cout << "Validate control pointer." << std::endl; drin. Probier sowas bei deinem Code zu implementieren und zwar nur für Zeiger 😉

    Grüssli



  • Dravere schrieb:

    audacia schrieb:

    Dravere schrieb:

    @audacia,
    CFINAE? Was meinst du mit dem C? 🙂

    Compilation.

    Wieso hat das was mit Compilation und nicht mit Substitution zu tun?

    SFINAE betrifft nur die Überladungsauflösung, mithin die Funktionssignaturen und nicht die Implementation. Und wenn der Compiler ein Template instantiiert, muß er nicht nur Typen substituieren, sondern auch den Code compilieren 😉

    Dravere schrieb:

    Zudem, wieso wird die Funktion mit der Referenz gewählt, statt der Funktion mit dem Zeiger?

    Wüßte ich es, hätte ich oben geantwortet. Offenbar sieht der Standard diese Reihenfolge vor. Wir müssen dann eben damit leben.

    Dravere schrieb:

    Mein Code hat bei einem Zeiger ein assert und ein std::cout << "Validate control pointer." << std::endl; drin. Probier sowas bei deinem Code zu implementieren und zwar nur für Zeiger 😉

    Habe ich doch. Hast du ihn denn überhaupt ausprobiert?

    int main (void)
    {
        Control ctrl;
        Control* p = &ctrl;
    
        fooDecorator (p);
        fooDecorator (ctrl);
    }
    
    Validate control pointer.
    Ctrl value: {value of control}
    Ctrl value: {value of control}
    

  • Administrator

    audacia schrieb:

    Habe ich doch. Hast du ihn denn überhaupt ausprobiert?

    Es ist noch schlimmer, ich habe nicht richtig hingeschaut, denn wenn ich es getan hätte, hätte ich gesehen, dass der Code funktioniert, wie gewünscht. Dazu muss man nicht mal ausprobieren 😉

    *muss zum Augenarzt* ... *und das sogar tatsächlich* 🙂
    Naja, danke für die Hilfe. Ich denke, dass ich es schlussendlich auf die einfache Art lösen werde. Nur die Zeigerversion nehmen, denn 99% der Controls sind Zeiger.

    Grüssli


Log in to reply