27
votes

Consider this C++11 code:

#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 and Clang accept this as valid C++11 code, but Visual Studio 2013 requires the lambda to be declared mutable (auto foo = [f]() mutable { ... }). Otherwise I get this error:

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

Is Visual Studio right to reject this code without mutable, or is it valid C++11?

(Curiously Clang rejects the code if you change std::bind(std::puts, "hello") to std::bind(std::exit, 0) apparently because it considers noreturn to make the function type different; I'm quite sure this is a bug.)

2

2 Answers

14
votes

This isn't really about lambdas.

#include <functional>
#include <cstdlib>

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

This is accepted by GCC, but rejected by MSVC.

The standard is unclear on whether this is valid, IMO. The return value g of std::bind has an unspecified return type for which g(...) is valid and defined in terms of the cv-qualifiers of g, but the standard doesn't actually say that any operator() must be callable for const-qualified objects or references. It strongly implies that this is intended to be valid, because otherwise the reference to g's cv-qualifiers seems useless, but it doesn't actually say it is valid.

Because of that, I think MSVC's behaviour is not what the standard's authors intended, but it may nonetheless conform to what the standard requires.

10
votes

This looks like a bug in the Visual Studio implementation of bind, returning a type with only a non-const function call operator. It should return a type that forwards all function calls to the bound function object, regardless of its own cv-qualifications.

To summarise the rather opaque language of C++11 20.8.9.1.2, function calls on the result of bind should be forwarded to the bound function object, and so should be allowed if calls on that object would be allowed. So it would be an error if the bound function object weren't callable if const; but here, being a function pointer, it is callable regardless of cv-qualifications.