force_cast ambiguous?



  • Moin moin,

    Ok, kleines Problem mim gcc (3.4+, 4.0 und 4.1, praktisch alle Versionen). Wieso meldet er, dass folgender Aufruf ambiguous ist? (verkürzter Codeausschnitt)

    template <typename DestinationType, typename SourceType>
    inline DestinationType& force_cast(SourceType& source)
    {
    	return *reinterpret_cast<DestinationType*>(&source);
    }
    
    template <typename DestinationType, typename SourceType>
    inline const DestinationType& force_cast(const SourceType& source)
    {
    	return *reinterpret_cast<const DestinationType*>(&source);
    }
    
    // -------------------------------------------------------------------
    
    struct Message
    {
    	unsinged int		lParam_;
    };
    
    template
    <
    	unsigned int MessageID,
    	typename LPARAM = MessageParam,
    	typename RPARAM = MessageParam
    >
    struct CustomMessage : public Message
    {
    public:
    	inline const LPARAM getLParam() const;
    };
    
    template <unsigned int MessageID, typename LPARAM, typename RPARAM>
    inline const LPARAM CustomMessage<MessageID, LPARAM, RPARAM>::getLParam() const
    {
    	return force_cast<LPARAM>(static_cast<const unsinged int&>(lParam_));
    }
    

    Er kann sich nicht zwischen den 2 force_casts entscheiden.. eigentlich sollte er keine Wahl haben, force_cast wird aus einer const Methode aufgerufen und somit ist die Member Variable lParam_ const.. um sicher zu gehen, wird dieser noch einmal zu const unsigned int& gecastet (unnötig), ändert aber nichts an der Fehlermeldung Oo

    P.S.: Der MS VC schluckts ^^



  • Wenn Du bei der ersten für SourceType const LPARAM einsetzt, dann paßt es genauso gut wie wenn Du beim zweiten LPARAM einsetzt.

    Die zweite Funktion ist keine Spezialisierung des ersten templates... (template-Funktion lassen sich nämlich nicht spezialisieren), sondern eine Überladung. Daher sind beide aktiv.

    Du könntest aber versuchen die erste ungültig zu machen, indem Du beim Parameter das const entfernst, irgendwie so:

    template <typename T>
    class remove_const
    {
      typedef T value;
    };
    
    template <typename T>
    class remove_const<const T>
    {
      typedef T value;
    };
    
    inline DestinationType& force_cast(remove_const<SourceType>::value & source);
    

    oder so ähnlich. boost bietet solche (auch reference remove etc.) schon fertig an.

    Eine andere Lösung fällt mir im Moment nicht ein.

    MfG Jester



  • Schon klar, beide sind aktiv.. nur dürfte er den 1. doch nicht nehmen, da die Referenz const ist, der Param des 1. const_casts aber nicht => const_cast wäre in der Lage den Param zu ändern... oder nicht? Oo

    Gegenbeispiel:

    void test(int& foo)
    {
    }
    
    int main(int argc, char* argv[])
    {
    	int a = 5;
    	const int& b = a;
    	test(b);
    ...
    

    -> Error, cannot convert from 'const int&' to 'int&'... logischerweise...
    Wieso meint Herr gcc also, dass er es beim force_cast darf? Er hat keine Wahl, er darf doch nur das force_cast mit const Param nehmen... oder seh ich das falsch?



  • ahh verdammt, const_cast weg und force_cast hin 😉



  • Nein, er setzt für das SourceType im ersten Fall const LPARAM ein. Dann paßt es genau.



  • Die Fehlermeldung lautet im Konkreten (GCC 4.1/GCC 3.4) so:

    Sdk/Inc/OECoreMessage.inl:149: error: call of overloaded `force_cast(const unsigned int&)' is ambiguous
    Sdk/Inc/OEBaseTypes.h:61: note: candidates are: DestinationType& OE::force_cast(SourceType&) [with DestinationType = OE::Core::MessageDispatcherID, SourceType = const unsigned int]
    Sdk/Inc/OEBaseTypes.h:67: note:                 const DestinationType& OE::force_cast(const SourceType&) [with DestinationType = OE::Core::MessageDispatcherID, SourceType = unsigned int]
    

    Wenn ich dich richtig verstehe, dann ist das Problem nicht die Überladung der Funktionsparamter das eigentliche Übel sondern die Templateparameter. Ist das der Fall sollte die Fehlermeldung fast einen Hinweis drauf geben (GCC-Bugreport?) 😉

    Boost: Mit Google bin ich auf ein "force_cast" (praktisch gleich) gestoßen. Offiziell auf der Boost-Seite finde ich aber nichts von force_cast.



  • boost hat auch kein force_cast, sondern nur ein remove_const und sowas.

    Das ist kein Compiler-Fehler. Steht doch klar da was los ist. Nimm Dir die Funktion, schreib das rein was der Compiler dort einsetzt und Du wirst sehen, die Signatur stimmt bis auf den Rückgabetyp überein. Der Compiler hat recht, der Aufruf ist mehrdeutig.

    Ein Problem ist, daß bei der ersten Lösung, die ich angegeben habe, vermutlich template-argument-deduction schief geht.

    Was man machen könnte wäre sowas:

    template<typename T>
    class is_const
    {
      enum { value = false };
    };
    
    template<typename T>
    class is_const<const T>
    {
      enum { value = true };
    };
    
    template <typename T, bool addConst>
    class add_const
    {
      typedef T value;
    };
    
    template <typename T, bool addConst>
    class add_const<T, true>
    {
      typedef const T value;
    };
    
    template <typename DestinationType, typename SourceType>
    inline add_const_if<DestinationType, is_const<SourceType>::value >::value & force_cast(SourceType& source)
    {
        typedef add_const_if<DestinationType, is_const<SourceType>::value DestType;
        return *reinterpret_cast<DestType*>(&source);
    }
    

    Wenn also der Source-Type ein const mit sich bringt, dann hängen wir auch an den DestinationType eins dran.
    Damit sollte template-Argument-Deduction funktionieren, außerdem isses nur noch eine Funktion statt zweien.

    MfG Jester

    P.S.: nicht getestet 😉


Anmelden zum Antworten