lambda und std::is_constructable



  • Hallo,

    angenommen ich hab folgenden Code:

    #include <iostream>
    
    int main()
    {
    	int i = 0;
    	auto lambda = [=]{ return i; };
    
    	std::cout << std::boolalpha;
    	std::cout << std::is_constructible<decltype(lambda), int>::value << '\n'; // true
    }
    

    dann sagt mir std::is_contructable ja dass ich das lambda mit einem int erstellen darf... Wenn ich dann aber sowas schreibe:

    decltype(lambda) test(i);
    

    dann erhalte ich einen Fehler "error C3497: Es kann keine Instanz eines Lambdas erstellt werden".

    Aber per:

    auto test = lambda;
    

    kann ich doch eine solche Instanz erstellen, oder nicht?


  • Mod

    Ganz einfach: Die Implementierung von std::is_constructible wird wahrscheinlich einen Bug haben nachdem auch private Konstruktoren berücksichtigt werden. Eigentlich sollte expression-SFINAE o.ä. verwendet werden.



  • Ach so Ok, wenn das ein Bug ist dann passts ja.


  • Mod

    Das vom GCC generierte Programm hat die richtige Ausgabe, nämlich "false". Der nutzt eben SFINAE in seiner Implementierung von __is_nary_constructible .

    Der closure-Typ sieht (unoptimiert) wahrscheinlich etwa so aus:

    class __lambda654 // irgendein Name
    {
        int i;
    
        __lambda654(int i) : i(i){}
    
    public:
    
        // implizit generierter Kopierkonstruktor
    
        int operator()() const {return i;}
    };
    

    VC++ eben.



  • Arcoth schrieb:

    Ganz einfach: Die Implementierung von std::is_constructible wird wahrscheinlich einen Bug haben nachdem auch private Konstruktoren berücksichtigt werden. Eigentlich sollte expression-SFINAE o.ä. verwendet werden.

    Ich glaub ehrlich gesagt nicht, dass is_constructible private Ctors berücksichtigt.
    Da müsste man sich ja schon anstrengen um das hinzukriegen.
    Laut der Fehlermeldung glaub ich eher, dass der Ctor public ist und der Compiler einfach eine Fehlermeldung ausgibt, sobald man ihn aufrufen möchte.
    Denn konstruiert sieht sie auf jeden Fall aus.


  • Mod

    Da müsste man sich ja schon anstrengen um das hinzukriegen.

    Ich habe vollstes Vertrauen in VC++.

    Laut der Fehlermeldung glaub ich eher, dass der Ctor public ist und der Compiler einfach eine Fehlermeldung ausgibt, sobald man ihn aufrufen möchte.

    Ja, plausibel. Wie sieht denn die Implementierung von is_constructible beim VC++ aus? Laut deiner Theorie müsste durch ein Intrinsic implementiert sein.



  • Arcoth schrieb:

    Laut deiner Theorie müsste durch ein Intrinsic implementiert sein.

    Ist es. Wer schreibt den Bug report?


  • Mod

    Du natürlich :p



  • Nö. Ich bin mit dem Template- und C++11-Zeugs zu wenig vertraut.


  • Mod

    Und ich werde mich nicht bei Microsoft registrieren.

    Schreib sowas:

    This code:

    #include <type_traits>
    
    int main()
    {
        int i = 0;
        auto lambda = [=]{ return i; };
        static_assert( std::is_constructible<decltype(lambda), int>::value, "" );
    }
    

    shouldn't compile, because declaring an object like so:

    decltype(lambda) d(i);
    

    doesn't compile and isn't well-formed (which is exactly the condition that is_constructible should examine).

    The above code does compile though.

    It seems the implementation of std::is_constructible (or the intrinsic __is_constructible , respectively) is bugged and doesn't consider the compiler magic that happens to prevent the programmer from constructing a closure object in the aforementioned way.



  • Hallo happystudent,

    happystudent schrieb:

    ... Wenn ich dann aber sowas schreibe:

    decltype(lambda) test(i);
    

    dann erhalte ich einen Fehler "error C3497: Es kann keine Instanz eines Lambdas erstellt werden".

    Der VC++-Compiler scheint hier den Returnwert der lamda-Funktion und die lambda-Funktion selbst durcheinander zu würfeln.
    So übersetzt z.B.:

    decltype(lambda) test = i;
    

    Wenn man aber schreibt:

    decltype(lambda) test = irgendeinInt;
    

    dann gibt

    cout << "test() = " << test() << endl;
    

    stets den Wert von 'irgendeinInt' statt den von 'i' aus.

    Ein Hinweis darauf, dass der VC++-Compiler decltype(lambda) tatsächlich fälschlicherweise als int interpretiert, liefert auch:

    decltype(lambda) test = 0.12;
    

    mit der Warnung: " warning C4244: 'argument' : conversion from 'double' to 'const int', possible loss of data "

    Es gibt noch viel zu tun ...
    Gruß
    Werner


  • Mod

    Werner Salomon schrieb:

    Wenn man aber schreibt: […] dann gibt […] stets den Wert von 'irgendeinInt' statt den von 'i' aus.

    Und was genau soll daran verwunderlich sein? Das ist das erwartete Verhalten. Der closure-Typ hat einen Konstruktor der den Member i initialisiert.

    Ein Hinweis darauf, dass der VC++-Compiler decltype(lambda) tatsächlich fälschlicherweise als int interpretiert, liefert auch: […]

    Jeder Compiler gibt dir eine Warnung wenn du versuchst eine Ganzzahl mit einem Fließkomma-Literal zu initialisieren - in diesem Fall ist der int der Konstruktorparameter.

    Wir können aber deine Theorie per

    static_assert( std::is_convertible<decltype(lambda), int>::value, "" );
    

    überprüfen.



  • Arcoth schrieb:

    Werner Salomon schrieb:

    Wenn man aber schreibt: […] dann gibt […] stets den Wert von 'irgendeinInt' statt den von 'i' aus.

    Und was genau soll daran verwunderlich sein? Das ist das erwartete Verhalten.

    ich habe das nicht erwartet 😉

    Arcoth schrieb:

    Der closure-Typ hat einen Konstruktor der den Member i initialisiert.

    .. so scheint es zu sein, aber wo bitteschön in den Weiten des Standards ist das beschrieben?

    Gruß
    Werner


Anmelden zum Antworten