2014-09-24 4 views
27

Rozważmy następujący kod C++ 11:Czy C++ 11 wymaga aby lambda została uznana za zmienną?

#include <functional> 
#include <cstdlib> 

template <typename F> 
void test(F &&f) { 
    auto foo = [f]() { 
     f(); 
    }; 
    foo(); 
} 

int main() { 
    test(std::bind(std::puts, "hello")); 
    return 0; 
} 

GCC i Clang zaakceptować to jako ważnego kodu C++ 11, ale Visual Studio 2013 wymaga lambda należy deklarować zmienne (auto foo = [f]() mutable { ... }). W przeciwnym razie pojawia się ten błąd:

error C3848: expression having type ' const std::_Bind<true,int,int (__cdecl *const)(const char *),const char (&)[6]> ' would lose some const-volatile qualifiers in order to call ' int std::_Bind<true,int,int (__cdecl *const)(const char *),const char (&)[6]>::operator()<>(void) '

Czy program Visual Studio ma prawo odrzucić ten kod bez możliwości zmiany, czy też jest poprawny C++ 11?

(ciekawe Clang odrzuca kod po zmianie std::bind(std::puts, "hello") do std::bind(std::exit, 0) widocznie ponieważ uważa noreturn aby funkcja Typ różne;. Jestem pewien, że jest to bug)

Odpowiedz

14

To nie jest tak naprawdę o lambdas .

#include <functional> 
#include <cstdlib> 

int main() { 
    const auto x = std::bind(std::puts, "hello"); 
    x(); 
} 

Jest to akceptowane przez GCC, ale odrzucone przez MSVC.

Norma nie jest jasna, czy to jest ważne, IMO. Zwracana wartość g z std::bind ma nieokreślony typ zwracania, dla którego g(...) jest poprawny i zdefiniowany w kategoriach kwalifikatorów cv z g, ale standard nie mówi, że każdymusi być dostępny dla const -kwalifikowanych obiektów lub odniesień. Wyraźnie oznacza to, że ma to być poprawne, ponieważ w przeciwnym razie odniesienie do kwalifikatorów cv g wydaje się bezużyteczne, ale w rzeczywistości nie oznacza, że ​​jest poprawne.

Z tego powodu uważam, że zachowanie MSVC nie jest zgodne z zamierzeniami autorów standardu, ale może być zgodne z tym, czego wymaga standard.

+0

Nie, nie jest zgodny ze standardem. "Efektem" g (u1, u2, ..., uM) "będzie" INVOKE (fd, v1, v2, ..., vN, result_of :: type) '" oznacza, że ​​wynik 'bind' musi być możliwy do wywołania, jeśli obiekt funkcji bound jest. Wskaźnik funkcji można wywoływać bez względu na to, czy jest to "const", czy też "bind". –

+0

@MikeSeymour 'g' jest zwracaną wartością' std :: bind', i określone jest tylko zachowanie 'g (...)'. Gdzie standard wymaga wywoływania pomocniczego za pośrednictwem odniesienia "const" do tej wartości zwracanej? – hvd

+0

W cytacie podałem powyżej (20.8.9.1.2/3) oraz następującej klauzuli "gdzie' cv' reprezentuje kwalifikatory cv 'g'". Wywołanie 'g' ma taki sam efekt jak wywołanie' fd' (obiektu powiązanej funkcji) z kwalifikatorami cv 'g' zastosowanymi do' fd'; więc wywołanie do 'const' -kwalifikowanej kopii/odniesienia' g' musi być prawidłowe, jeśli wywołane jest 'const FD'. W tym przypadku 'FD' jest typem wskaźnika funkcji, który można wywoływać niezależnie od tego, czy jest to' const'. –

10

To wygląda jak błąd w implementacji Visual Studio z bind, zwracając typ z tylko niestanowiącym stałego operatora wywołania funkcji. Powinien zwrócić typ, który przekazuje wszystkie wywołania funkcji do powiązanego obiektu funkcji, niezależnie od własnych kwalifikacji cv.

Podsumowując dość nieprzejrzysty język C++ 11 20.8.9.1.2, wywołania funkcji na wyniku bind powinny zostać przekazane do obiektu funkcji powiązanej, a więc powinny być dozwolone, jeśli wywołania na tym obiekcie byłyby dozwolone . Byłby więc błędem, gdyby obiekt funkcji powiązanej nie był wywoływalny, jeśli: const; ale tutaj, będąc wskaźnikiem funkcji, można go wywoływać niezależnie od kwalifikacji cv.