SFINAE - IsExplicitConvertible und IsDefaultConstructible



  • Hallo,

    also zunächst mal würde ich gerne wissen, ob es allgemein korrekt ist (nach allen drei mehr oder weniger aktuellen Standards) einen Laufzeitausdruck auf Richtigkeit zu prüfen, indem man das folgende Konstrukt verwendet:

    template<typename T>
    		static NoType Check(...);
    		template<typename T>
    		static YesType Check(SizeToType<sizeof(Ausdruck)>*);
    

    Denn seltsamerweise funktionieren solche Konstrukte teilweise, teilweise hingegen auch nicht. Hier knüpft auch meine nächste Frage gerade an. Ist das Folgende korrekt?

    template<typename From, typename To>
    	class IsExplicitConvertible
    	{
    		template<typename FromT, typename ToT>
    		static NoType Check(...);
    		template<typename FromT, typename ToT>
    		static YesType Check(SizeToType<sizeof(static_cast<ToT>(DeclVal<FromT>()))>*);
    
    	public:
    		static const bool Value = sizeof(Check<From, To>(0)) == sizeof(YesType);
    	};
    

    Zur Erklärung kurz falls die Bezeichner nicht selbsterklärend genug sind:

    • SizeToType ist etwa dasselbe wie template<std::size_t Size> using SizeToType = std::integral_constant<std::size_t, Size>;
    • NoType und YesType sind garantiert unterschiedich grosse Typen

    Der Code zu IsExplicitConvertible funktioniert auf MSVC nicht, auf GCC 4.8.1 hingegen schon. Wäre er also eigentlich korrekt?

    Nun zu einem Code, der sowohl auf MSVC als auch auf GCC nicht das gewünschte Ergebnis liefert:

    template<typename T>
    		class IsDefaultConstructible
    		{
    			template<typename U>
    			static NoType Check(...);
    			template<typename U>
    			static YesType Check(SizeToType<sizeof(U())>*);
    
    		public:
    			static const bool Value = sizeof(Check<T>(0)) == sizeof(YesType);
    		};
    

    Interessant ist jedoch, dass dieser Code bei MSVC bei allen Typen in true resultiert, bei GCC jedoch in false . Wäre der Code korrekt?

    Hinzuzufügen ist vielleicht noch, dass bei MSVC die meisten Klassen aus <type_traits> mit Intrinsics implementiert wurden.

    Gruss.

    Edit:
    Hier ist das Minimalbeispiel:

    #include <iostream>
    
    typedef std::size_t SizeType;
    
    template<typename T>
    struct Identity
    {
    	typedef T Type;
    };
    
    typedef Identity<char(&)[1]>::Type NoType;
    typedef Identity<char(&)[2]>::Type YesType;
    
    template<typename T, T Val>
    struct IntegralConstant : public Identity<IntegralConstant<T, Val> >
    {
    	typedef T ValueType;
    
    #ifdef Lib_Cpp11
    	static constexpr ValueType Value = Val;
    	constexpr operator ValueType() const
    	{
    		return this->Value;
    	}
    #else // Lib_Cpp11
    	static const ValueType Value = Val;
    	operator ValueType() const
    	{
    		return this->Value;
    	}
    #endif // Lib_Cpp11
    };
    
    template<SizeType Val>
    struct SizeToType : public IntegralConstant<SizeType, Val>
    {
    };
    
    template<bool Val>
    struct BoolToType : public IntegralConstant<bool, Val>
    {
    };
    
    template<typename T>
    T& DeclVal();
    
    namespace Detail
    {
    	template<typename From, typename To>
    	class IsExplicitConvertible
    	{
    		template<typename FromT, typename ToT>
    		static NoType Check(...);
    		template<typename FromT, typename ToT>
    		static YesType Check(SizeToType<sizeof(static_cast<ToT>(DeclVal<FromT>()))>*);
    
    	public:
    		static const bool Value = sizeof(Check<From, To>(0)) == sizeof(YesType);
    	};
    
    	template<typename T> 
    	class IsDefaultConstructible 
    	{ 
    		template<typename U> 
    		static NoType Check(...); 
    		template<typename U> 
    		static YesType Check(SizeToType<sizeof(U())>*); 
    
    	public: 
    		static const bool Value = sizeof(Check<T>(0)) == sizeof(YesType); 
    	};
    }
    
    template<typename From, typename To>
    struct IsExplicitConvertible : public BoolToType<Detail::IsExplicitConvertible<From, To>::Value>
    {
    };
    
    template<typename T>
    struct IsDefaultConstructible : public BoolToType<Detail::IsDefaultConstructible<T>::Value>
    {
    };
    
    struct Foo1
    {
    	explicit Foo1(int);
    
    	operator char();
    };
    
    class Foo2
    {
    	Foo2();
    };
    
    struct Foo3
    {
    };
    
    int main()
    {
    	std::cout << IsExplicitConvertible<int, float>::Value << '\n';		// Sollte 1 sein
    	std::cout << IsExplicitConvertible<int, Foo1>::Value << '\n';		// Sollte 1 sein
    	std::cout << IsExplicitConvertible<float, Foo1>::Value << '\n';		// Sollte 1 sein
    	std::cout << IsExplicitConvertible<Foo1, char>::Value << '\n';		// Sollte 1 sein
    	std::cout << IsExplicitConvertible<Foo1, double>::Value << '\n';	// Sollte 1 sein
    	std::cout << IsExplicitConvertible<bool, Foo2>::Value << '\n';		// Sollte 0 sein
    	std::cout << IsExplicitConvertible<int, void*>::Value << '\n';		// Sollte 0 sein
    	std::cout << '\n';
    	std::cout << IsDefaultConstructible<int>::Value << '\n';			// Sollte 1 sein
    	std::cout << IsDefaultConstructible<Foo1>::Value << '\n';			// Sollte 0 sein
    	std::cout << IsDefaultConstructible<Foo2>::Value << '\n';			// Sollte 0 sein
    	std::cout << IsDefaultConstructible<Foo3>::Value << '\n';			// Sollte 1 sein
    }
    

    http://ideone.com/aFlCpm

    Edit 2:
    Den Blödsinn rausgelöscht. void ist natürlich nicht in Default-Konstruierbare Typen konvertierbar. Ich dachte, dass das mit dem Übergeben eines void s (wie bei Funktionen) auch bei Konstruktoren funktioniert, dem ist aber nicht so.



  • Mir fehlt das lauffähige Programm zum Testen.


  • Mod

    Wäre er also eigentlich korrekt?

    Er ist korrekt.

    Wäre der Code korrekt?

    Nein, der zweite Code ist ganz subtil falsch.

    sizeof(U())
    

    U() ist ein Funktionstyp. Und man darf sizeof nicht auf Funktionstypen anwenden.

    §5.3.3/1 schrieb:

    The sizeof operator shall not be applied to an expression that has function or incomplete type

    Daher wird die Instantiierung des zweiten Funktionstemplates immer in einem deduction failure enden.



  • gcc hat übrigens den sutilen fehler angewarnt.
    "invalid application of 'sizeof' to a function type"

    Und dann kapiert gcc Folgendes:

    static YesType Check(SizeToType<sizeof((U()))>*);
    

  • Mod

    gcc hat übrigens den sutilen fehler angewarnt.

    Kann ich nicht nachvollziehen. Welcher Code?



  • volkard schrieb:

    Mir fehlt das lauffähige Programm zum Testen.

    Ups, sorry. Ich hab's ergänzt.

    Arcoth schrieb:

    U() ist ein Funktionstyp.

    😮 Wow, das ist gemein.

    volkard schrieb:

    Und dann kapiert gcc Folgendes:

    Funktioniert das, weil man Typen nicht in ein Klammerpaar setzen darf, Werte aber schon? Cooler Trick.



  • Arcoth schrieb:

    gcc hat übrigens den sutilen fehler angewarnt.

    Kann ich nicht nachvollziehen. Welcher Code?

    cout<<sizeof(Foo1())<<'\n';
    

Anmelden zum Antworten