Problem mit SFINAE



  • Hiho,

    ich möchte gerne mit SFINAE eine Typunterscheidung machen. Wenn meine Template-Funktion "make_with_oob" funktioniert (also keinen Compilefehler wirft), möchte ich einen bestimmten Typen haben. Ansonsten soll ein Standardtyp genutzt werden.

    //
    template<typename T, typename Enable = void>
    struct Oobb_type_check
    {
    	using value = T;
    };
    
    //
    template
    <
    	typename T,
    	typename decltype(make_with_oobb(T()))
    >
    struct Oobb_type_check
    {
    	using value = Object_with_oobb<T>;
    };
    

    Das VS2019 sagt mir aber:
    error C3855: "matrem::Oobb_type_check": Vorlage-Parameter "Enable" ist mit der Deklaration nicht kompatibel.

    Ich seh grad meinen Fehler nicht. Kann mir jemand sagen, was ich falsch mache?

    VG

    Pellaeon



  • OK, der Code oben ist falsch, da ich keine Spezialisierung mache. Ich habe jetzt den folgenden Ablauf, welcher aber leider auch noch nicht geht.

    Ausgangslage sind die beiden folgenden Aufrufe:

    octree.subtract(matrem::get_with_optional_oobb(matrem::make_cylinder({ data[i].x, data[i].y, data[i].z }, { 0, 0, 1 }, 4, 20)));
    octree.subtract(matrem::get_with_optional_oobb(matrem::make_sphere({ data[i].x, data[i].y, data[i].z }, 4)));
    

    Für Zylinder gibt es eine OOBB, für die Kugel nicht. Das ist die Fallunterscheidung, die ich per template auflösen will.
    get_with_optional_oob ist daher so definiert:

    template<typename Type_t>
    inline auto get_with_optional_oobb(Type_t&& obj) -> typename Oobb_type_check<Type_t>::value		
    {
    	return Oobb_type_check<Type_t>::get(std::forward<Type_t>(obj));
    }
    

    Bei Oobb_type_check gibt es nun die folgende Unterscheidung:

    //
    template<typename T, typename Enable = void>
    struct Oobb_type_check
    {
    	//!
    	using value = T&;
    
    	//!
    	template<typename Obj_t>
    	static T& get(Obj_t&& obj) { return obj; }
    };
    
    //
    template<typename T>
    struct Oobb_type_check<T, typename std::enable_if_t<std::is_same<Object_with_oobb<T>, decltype(make_with_oobb(T()))>::value>>
    {
    	//!
    	using value = Object_with_oobb<T>;
    
    	//!
    	template<typename Obj_t>
    	static value get(Obj_t&& obj) { return make_with_oobb(obj); }
    };
    

    Das allgemeine Template geht davon aus, dass es keine OOBB gibt und gibt das Objekt daher einfach wieder zurück.
    Die Spezialisierung dagegen versucht, einen Typ mit decltype abzuleiten: decltype(make_with_oobb(T())).
    Geht der Asufruf durch, soll die Spezialisierung genommen werden, ansonsten das allgemeine drüber.

    Für einen Typ, wo make_with_oobb nicht existiert und der Aufruf fehlschlägt, geht der Compiler aber nicht zur allgemeinen Varianten, sondern bringt einen Fehler:

    error C2665: "matrem::get_oobb": Durch keine der 2 Überladungen konnten alle Argumenttypen konvertiert werden. (Quelldatei wird kompiliert allocator_benchs.cpp)

    Wo ist der Fehler in meinem Ablauf?

    VG



  • Kannst du ein Beispiel erstellen, dass wir via Copy&Paste kopieren können und den Compiler Fehler direkt nachvollziehen können?



  • ich hab mal ein beispiel mit std::is_convertible gemacht:

    struct zylinder
    {
    };
    
    struct kugle
    {
    };
    
    
    template<class T>
    struct oobb
    {
       oobb(const T &z) : obj(z)
       {
       }
    
       operator zylinder(void) const 
       {
          return obj;
       }
    
       T obj;
    };
    
    template<class T, bool b>
    struct wrapper;
    
    template<class T>
    struct wrapper<T, true>
    {
       wrapper(const T &t_obj) : obj(t_obj)
       {
       }
    
       auto get(void) -> oobb<T>
       {
          return oobb(obj);
       }
    
       T obj;
    };
    
    template<class T>
    struct wrapper<T, false>
    {
       wrapper(const T &t_obj) : obj(t_obj)
       {
       }
    
       auto get(void) -> const T&
       {
          return obj;
       }
    
       T obj;
    };
    
    template<class T>
    inline auto print_obj(const wrapper<T, false> &wr) -> void
    {
       std::cout << "standard object" << std::endl;
    }
    
    template<class T>
    inline auto print_obj(const wrapper<T, true> &wr) -> void
    {
       std::cout << "oobb optimized" << std::endl;
    }
    
    template<class T>
    inline auto print_info(const T &obj) -> void
    {
       print_obj(wrapper<T, std::is_convertible<oobb<T>, T>::value>(obj));
    }
    print_info(zylinder());
    print_info(kugle());
    

    ausgabe:

    oobb optimized
    standard object
    

    Meep Meep



  • @Meep-Meep sagte in Problem mit SFINAE:

    std::is_convertible

    Hiho,

    das ist natürlich auch noch ein Ansatz. Wobei ich an sich keine Konvertierungsoperatoren haben wollte. Es sollen an sich schon immer die "make_..."-Funktionen aufgerufen werden. Aber ich teste das mal als Option. Von daher erst mal vielen Dank für die Idee.

    Aber kannst du mir sagen, was in meinem Code oben verkehrt ist (in meinem 2. Post). Ich habe ja die allgemeine Template-struct Oobb_type_check und die Spezialisierung, welche das make_oobb nutzt. Ich hätte gedacht, wenn die make_oobb funktioniert, dass diese Template-Struct genommen wird. Wenn die Funktion aber einen Compile-Fehler wirft, dass dann durch SFINAE das allgemeine Template genutzt wird. Funktioniert aber leider nicht und ich verstehe nicht warum



  • @Pellaeon Eine Fehlermeldung zu einer Funktion posten deren Definition du uns nicht zeigst, ist nicht sehr sinnvoll.

    Dann... das typename in typename std::enable_if_t ist auf jeden Fall schonmal unnötig und IMO auch verwirrend.

    Davon abgesehen...
    Keine Ahnung was du anstellst, das

    #include <typeinfo>
    #include <utility>
    #include <vector>
    #include <list>
    #include <string>
    
    std::vector<int> package(int const&);
    std::list<std::string> package(std::string const&);
    
    template<typename T, typename Enable = void>
    struct Foo
    {
        static const int i = 0;
    };
    
    template<typename T>
    struct Foo<T,
            std::enable_if_t<
                std::is_same_v<
                    std::vector<T>,
                    decltype(package(std::declval<T>()))
                >
            >
        >
    {
        static const int i = 1;
    };
    
    int test0() {
        return Foo<double>::i;
    }
    
    int test1() {
        return Foo<int>::i;
    }
    

    https://godbolt.org/z/-OQAGs
    geht auf jeden Fall.


Log in to reply