С++ 11 требует, чтобы этот лямбда был объявлен изменчивым?
Рассмотрим этот код С++ 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 принимают это как действительный код С++ 11, но Visual Studio 2013 требует, чтобы лямбда была объявлена изменчивой (auto foo = [f]() mutable { ... }
). В противном случае я получаю эту ошибку:
ошибка C3848: выражение, имеющее тип 'const std::_Bind<true,int,int (__cdecl *const )(const char *),const char (&)[6]>
', потеряло бы некоторые константно-летные квалификаторы, чтобы вызвать 'int std::_Bind<true,int,int (__cdecl *const )(const char *),const char (&)[6]>::operator ()<>(void)
'
Является ли Visual Studio правом отклонять этот код без изменчивости, или это действительно С++ 11?
(Любопытно, что Clang отклоняет код, если вы меняете std::bind(std::puts, "hello")
на std::bind(std::exit, 0)
, по-видимому, потому, что он считает, что noreturn
делает тип функции другим, я совершенно уверен, что это ошибка.)
Ответы
Ответ 1
Это не о лямбдах.
#include <functional>
#include <cstdlib>
int main() {
const auto x = std::bind(std::puts, "hello");
x();
}
Это принято GCC, но отклонено MSVC.
В стандарте неясно, действительно ли это, ИМО. Возвращаемое значение g
of std::bind
имеет неопределенный тип возврата, для которого g(...)
действителен и определен в терминах cv-квалификаторов g
, но стандарт фактически не говорит, что любой operator()
должен быть вызванным для const
-qualified объектов или ссылок. Это сильно означает, что это должно быть действительно, потому что в противном случае ссылка на g
cv-квалификаторы кажется бесполезной, но на самом деле она не говорит, что она действительна.
Из-за этого я считаю, что поведение MSVC не соответствует стандартным авторам, но, тем не менее, может соответствовать требованиям стандарта.
Ответ 2
Это выглядит как ошибка в реализации Visual Studio bind
, возвращая тип только с оператором вызова неконстантной функции. Он должен возвращать тип, который пересылает все вызовы функций объекту связанной функции, независимо от собственной квалификации cv.
Чтобы обобщить довольно непрозрачный язык С++ 11 20.8.9.1.2, вызовы функций на результат bind
должны быть перенаправлены на объект связанной функции и поэтому должны быть разрешены, если вызовы на этом объекте будут позволил. Таким образом, это была бы ошибка, если объект связанной функции не был бы разрешен, если const
; но здесь, будучи указателем функции, он может быть вызван независимо от cv-квалификаций.