2014-09-24 2 views
27

Рассмотрим C++ 11 код:Требует ли C++ 11, чтобы этот лямбда был объявлен изменчивым?

#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 и Clang принять это, как действительный C++ 11 кода, но Visual Studio 2013 требует лямбда быть объявлены изменяемыми (auto foo = [f]() mutable { ... }). В противном случае я получаю эту ошибку:

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) '

Является ли Visual Studio правом отклонить этот код без изменчивости, или это действительно C++ 11?

(Любопытно Clang отвергает код, если вы измените std::bind(std::puts, "hello") на std::bind(std::exit, 0), по-видимому, потому что он считает noreturn сделать тип функции различны;. Я совершенно уверен, что это ошибка)

ответ

14

Это на самом деле не о лямбдах ,

#include <functional> 
#include <cstdlib> 

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

Это принято GCC, но отклонено MSVC.

Стандарт неясно, действительно ли это, ИМО. Возвращаемое значение g из std::bind имеет неопределенный тип возврата, для которого g(...) действителен и определен в терминах cv-квалификаторов g, но стандарт фактически не говорит о том, что любой operator() должен быть вызван для const -qualified objects or reference. Это сильно означает, что это должно быть справедливым, поскольку в противном случае ссылка на cv-квалификаторы g представляется бесполезной, но на самом деле она не говорит, что она действительна.

Из-за этого, я думаю, что поведение MSVC не то, что авторы стандарта предполагают, но оно тем не менее может соответствовать тому, что требует стандарт.

+0

Нет, это не соответствует стандарту. «Эффект' g (u1, u2, ..., uM) 'должен быть' INVOKE (fd, v1, v2, ..., vN, result_of :: type) '" означает, что результат 'bind' должен быть вызван, если объект связанной функции. Указатель функции может быть вызван независимо от того, является ли он 'const', поэтому обязательная вещь' bind' тоже должна быть. –

+0

@MikeSeymour 'g' - это возвращаемое значение' std :: bind', и указано только поведение 'g (...)'. Где стандарт требует поддержки вызовов с помощью ссылки на const, соответствующей этому возвращаемому значению? – hvd

+0

В приведенной выше цитате (20.8.9.1.2/3) плюс следующее предложение «где' cv' представляет cv-квалификаторы 'g'. Вызов 'g' имеет тот же эффект, что и вызов' fd' (объект связанной функции) с cv-квалификаторами 'g', примененными к' fd'; поэтому вызов соответствующей 'const' -копии/ссылки' g' должен быть действительным, если вызывается вызов 'const FD'. Здесь «FD» - это тип указателя функции, который является вызываемым независимо от того, является ли он 'const'. –

10

Это выглядит как ошибка в реализации Visual Studio bind, возвращая тип только с помощью оператора неконстантной функции. Он должен возвращать тип, который переадресует все вызовы функций объекту связанной функции, независимо от его собственных CV-квалификаций.

Чтобы обобщить довольно непрозрачный язык C++ 11 20.8.9.1.2, вызовы функций по результату bind должны быть перенаправлены на связанный функциональный объект и поэтому должны быть разрешены, если вызовы на этом объекте будут разрешены , Таким образом, было бы ошибкой, если объект связанной функции не вызывался, если const; но здесь, будучи указателем на функцию, он может быть вызван независимо от cv-квалификаций.