C++0x Lambdas: "Recapturing"?



  • Ich möchte gerne in einem Lambda die gecapturete Referenz gleich an eine weitere geschachtelte Lmabda abgeben:

    const std::array<InterfaceEvent, 3> pattern = 
      {
        CREATE_EVENT(InterfaceEvent, CUSTOM, "N1"),
        CREATE_EVENT(InterfaceEvent, CUSTOM, "N2"),
        CREATE_EVENT(InterfaceEvent, CUSTOM, "N3")
      };
    
      InterfaceEventQueue queue;
      BOOST_CHECK(!queue.hasEvent());
      boost::thread pusher([&queue, pattern]()
      {
        //InterfaceEventQueue& q = queue;
        std::for_each(pattern.begin(), pattern.end(), [&queue](InterfaceEvent const& evt) //(!)
        {
          queue.pushEvent(evt);
        });
      });
    
      /* ... */
    

    Ich kriege im MSVC in der Zeile mit dem //(!) jetzt folgende Meldung:

    \test_interfaceeventqueue.cpp(49): error C3480: 'test_InterfaceEventQueue::`anonymous-namespace'::<lambda0>::queue': a lambda capture variable must be from an enclosing function scope
    

    Aus dem Grund habe ich die (hier auskommentierte) Zeile mit der Referenz zur "Umbenennung" von queue eingebaut und gebe stattdessen q an die innere Lambda.

    Meine Frage ist jetzt: Ist das Standardkonform, und macht gcc das auch? (Mein gcc-Versuchskaninchen ideone hat grade die Grätsche gemacht, hoffentlich war ichs nicht)
    Ich hätte eigentlich erwartet, dass die Namen weitergereicht werden können - wäre nicht der erste Bug im Name-Lookup mit Lambdas, über den ich im MSVC 2010 stolpere...


  • Administrator

    Scheint eine ganz witzige Sache zu sein. Aber vorweg was anderes: Ich bin müde, habe letzte Nacht kaum schlafen können und bin noch nicht wirklich sattelfest im neuen Standard. Also die folgenden Aussagen mit Vorsicht geniessen 😃

    Also meiner Meinung nach sollte dies problemlos funktionieren. Ich stütze mich dabei vor allem auf §5.1.2 Lambda Expressions, Clause 16.

    If a lambda-expression m2 captures an entity and that entity is captured by an immediately enclosing lambda-expression m1, then m2’s capture is transformed as follows:
    — if m1 captures the entity by copy, m2 captures the corresponding non-static data member of m1’s closure type;
    — if m1 captures the entity by reference, m2 captures the same entity captured by m1.

    [ Example: the nested lambda expressions and invocations below will output 123234.

    int a = 1, b = 1, c = 1;
    auto m1 = [a, &b, &c]() mutable {
      auto m2 = [a, b, &c]() mutable {
        std::cout << a << b << c;
        a = 4; b = 4; c = 4;
      };
      a = 3; b = 3; c = 3;
      m2();
    };
    a = 2; b = 2; c = 2;
    m1();
    std::cout << a << b << c;
    

    - end example]

    Einfache Beispielanwendung:

    #include <iostream>
    
    int main()
    {
      int x = 0;
    
      auto outer = [&x]() {
        auto inner = [&x]() {
          ++x;
          std::cout << x << '\n';
        };
    
        inner();
      };
    
      std::cout << x << '\n';
    
      outer();
    
      std::cout << x << '\n';
    
      return 0;
    }
    

    Kompiliert unter GCC 4.6.1 einwandfrei. Unter VS2010 gibt es den angegebenen Fehler.

    Jetzt zum witzigen Teil:
    23.05.2010 -> http://connect.microsoft.com/VisualStudio/feedback/details/560907/capturing-variables-in-nested-lambdas

    -> Bug bestätigt, Workaround bekannt (nämlich deiner), won't fix in next release

    07.06.2010 -> http://connect.microsoft.com/VisualStudio/feedback/details/565418/visual-c-project-can-run-with-error-reported

    -> Bug nicht bestätigt, by Design, berufen sich auf:
    http://www.open-std.org/Jtc1/sc22/wg21/docs/papers/2009/n2927.pdf 5.1.1 Clause 9
    Was aber veraltet ist und im aktuellen Standard so nicht mehr drin steht.

    Und nun noch zum letzten Workaround:

    #include <iostream>
    
    int main()
    {
      int x = 0;
    
      auto outer = [&]() {
        auto inner = [&]() {
          ++x;
          std::cout << x << '\n';
        };
    
        inner();
      };
    
      std::cout << x << '\n';
    
      outer();
    
      std::cout << x << '\n';
    
      return 0;
    }
    

    Funktioniert unter VS2010 und GCC 4.6.1.

    Meiner Meinung nach definitiv ein Fehler von MSVC. Sollte man vielleicht als Bug melden. Ausser natürlich ich hätte nun etwas in meiner Müdigkeit übersehen, daher bitte ich dringend um eine Gegenprüfung 🙂

    Grüssli



  • Ist hier mit "won't fix in next release" auch ncoh der VC12 gemeint?

    MfG SideWinder



  • SideWinder schrieb:

    Ist hier mit "won't fix in next release" auch ncoh der VC12 gemeint?

    MfG SideWinder

    Ich nehme mal an, "Next release" ist VC11, wo sie kleinere Bugs nicht fixen wollen.

    @Dravere: http://www.open-std.org/Jtc1/sc22/wg21/docs/papers/2009/n2957.html "beschwert" sich bereits über n2927. ALlerdings stellt sich die Frage, wann der Draft entsprechend geändert wurde, dass Recapturing möglich wäre. Der MS-Bug ist bereits ende Juni 2010 geschlossen worden, danach kann noch vieles passiert sein.



  • SideWinder, meinst du VC12 oder VS 2012? 🙂


Log in to reply