考虑这个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要求将lambda声明为可变(auto foo = [f]() mutable { ... }
)。否则我收到此错误:
错误C3848:类型为“
const std::_Bind<true,int,int (__cdecl *const )(const char *),const char (&)[6]>
”的表达式会丢失一些const-volatile限定符,以便调用“int std::_Bind<true,int,int (__cdecl *const )(const char *),const char (&)[6]>::operator ()<>(void)
”
Visual Studio是否正确拒绝此代码而没有可变,或者它是否有效C ++ 11?
(如果您将std::bind(std::puts, "hello")
更改为std::bind(std::exit, 0)
,Curiously Clang会拒绝代码,因为它认为noreturn
使函数类型不同;我很确定这是一个错误。)
答案 0 :(得分:14)
这不是关于lambdas的。
#include <functional>
#include <cstdlib>
int main() {
const auto x = std::bind(std::puts, "hello");
x();
}
这被GCC接受,但被MSVC拒绝。
该标准尚不清楚这是否有效,IMO。 g
的返回值std::bind
有一个未指定的返回类型,g(...)
有效,并根据g
的cv限定符定义,但标准不实际上,任何operator()
都必须可以调用const
- 限定对象或引用。它强烈暗示这是有效的,因为否则对g
的cv限定符的引用似乎没用,但它实际上并没有说它是有效的。
正因为如此,我认为MSVC的行为不是标准作者的意图,但它可能符合标准的要求。
答案 1 :(得分:10)
这看起来像bind
的Visual Studio实现中的一个错误,返回一个只有非const函数调用运算符的类型。它应该返回一个类型,它将所有函数调用转发给绑定的函数对象,而不管它自己的cv资格。
总结C ++ 11 20.8.9.1.2中相当不透明的语言,对bind
的结果的函数调用应该转发到绑定的函数对象,因此如果对该对象的调用应该允许会被允许的。因此,如果const
,绑定的函数对象不可调用则会出错;但是在这里,作为一个函数指针,无论cv资格如何都可以调用它。