2016-12-31 58 views
1


我正在使用VC++编译我的程序(使用Visual Studio 2015,更新3),并且某些代码段无法编译。std :: bind无法与std :: atomic_bool和MSVC编译

基本上,我想绑定一个函数,该函数获取对原子布尔值的引用与原子布尔值。自包含的代码:

void stub(std::atomic_bool& b) { 
    b = true; 
} 

int main() { 
    std::atomic_bool b(false); 
    std::function<void()> delegate = std::bind(stub, b); //fails to compile 

    auto& ref = b; 
    std::function<void()> delegate0 = std::bind(stub, ref); //fails to compile 

    std::function<void()> delegate1 = std::bind(stub, std::ref(b)); //compiled 
/*...*/ 
    } 

编译器堆栈跟踪:

1>c:\program files (x86)\microsoft visual studio 14.0\vc\include\xutility(357): error C2665: 'std::tuple<std::atomic<bool>>::tuple': none of the 2 overloads could convert all the argument types 
1> c:\program files (x86)\microsoft visual studio 14.0\vc\include\tuple(608): note: could be 'std::tuple<std::atomic<bool>>::tuple(std::tuple<std::atomic<bool>> &&)' 
1> c:\program files (x86)\microsoft visual studio 14.0\vc\include\tuple(607): note: or  'std::tuple<std::atomic<bool>>::tuple(const std::tuple<std::atomic<bool>> &)' 
1> c:\program files (x86)\microsoft visual studio 14.0\vc\include\xutility(357): note: while trying to match the argument list '(std::atomic<bool>)' 
1> c:\program files (x86)\microsoft visual studio 14.0\vc\include\functional(866): note: see reference to function template instantiation 'std::_Compressed_pair<void (__cdecl *)(std::atomic_bool &),std::tuple<std::atomic<bool>>,false>::_Compressed_pair<void(__cdecl &)(std::atomic_bool &),_Cv_TiD&>(std::_One_then_variadic_args_t,_Other1,_Cv_TiD &)' being compiled 
1>   with 
1>   [ 
1>    _Cv_TiD=std::atomic<bool>, 
1>    _Other1=void (__cdecl &)(std::atomic_bool &) 
1>   ] 
1> c:\program files (x86)\microsoft visual studio 14.0\vc\include\functional(864): note: see reference to function template instantiation 'std::_Compressed_pair<void (__cdecl *)(std::atomic_bool &),std::tuple<std::atomic<bool>>,false>::_Compressed_pair<void(__cdecl &)(std::atomic_bool &),_Cv_TiD&>(std::_One_then_variadic_args_t,_Other1,_Cv_TiD &)' being compiled 
1>   with 
1>   [ 
1>    _Cv_TiD=std::atomic<bool>, 
1>    _Other1=void (__cdecl &)(std::atomic_bool &) 
1>   ] 
1> c:\program files (x86)\microsoft visual studio 14.0\vc\include\functional(863): note: while compiling class template member function 'std::_Binder<std::_Unforced,void (__cdecl &)(std::atomic_bool &),std::atomic_bool &>::_Binder(_Fx,std::atomic_bool &)' 
1>   with 
1>   [ 
1>    _Fx=void (__cdecl &)(std::atomic_bool &) 
1>   ] 
1> c:\program files (x86)\microsoft visual studio 14.0\vc\include\functional(890): note: see reference to function template instantiation 'std::_Binder<std::_Unforced,void (__cdecl &)(std::atomic_bool &),std::atomic_bool &>::_Binder(_Fx,std::atomic_bool &)' being compiled 
1>   with 
1>   [ 
1>    _Fx=void (__cdecl &)(std::atomic_bool &) 
1>   ] 
1> c:\visual studio 2015\projects\quantum\quantum\main.cpp(658): note: see reference to class template instantiation 'std::_Binder<std::_Unforced,void (__cdecl &)(std::atomic_bool &),std::atomic_bool &>' being compiled 

有什么我错过或它的编译器的错吗?

+3

原子不可复制。这就是为什么你需要'std :: ref'。 –

+0

@KerrekSB为什么程序试图复制原子?该函数获取参数作为参数,原子作为参考传递,看不到在参考旁边被复制的内容 –

+0

看起来,您对C++类型和对象模型存在根本的误解。你*永远*“复制引用”。你总是*复制值*。值从来不是参考。引用是一种*变量*,您可以使用它来指定值。所以,当你说'ref'时,这是'std :: atomic_bool'类型的值(它恰好与'b'的值相同)。当你说'std :: ref(b)'(或'std :: ref(ref)')时,这是一个'std :: reference_wrapper '和'std :: bind'有一个协议来解释这一点。 –

回答

2

bind始终尝试存储的参数,从不参考。 atomic类型不能被复制。所以当bind试图复制它们时,它会失败。

这是reference_wrapper存在的原因之一:允许在需要值的地方使用对象的引用。事实上,std::ref发明主要是为了处理bind

请参阅,bind可能已存储对参数的引用。但是,存储引用可能非常危险,特别是对调用bind函数之前可能会停止存在的堆栈变量的引用。所以bind强制你在明确关于何时存储引用;它使你使用ref

+0

@DavidHaim:引用不是对象。 'bind'将始终存储值;它是复制还是移动它取决于你传递给函数的内容。但它会存储值,而不是引用。 –

+0

@Nicole Bolas,我不一定同意你的逻辑。 C++是不安全的语言。你仍然可以从一个函数返回一个指针或一个对局部变量的引用。你可以在绑定表达式中存储一个悬挂指针。禁用该成本的参考是没有意义的。 –

+0

@DavidHaim:“*我不一定同意你的逻辑。”*同意或不同意所有你想要的,但这就是为什么Boost(和标准库版本)这样做。我只是关于他们在这件事上的立场。仅仅因为语言不安全并不意味着你应该去创造更多不安全的东西。 C++毕竟要求你在返回这些东西时使用'&'或'*',所以至少你可以知道什么时候有什么可能是不安全的。默认情况下,C++传递/返回值。 –

1

std::atomic类型不是CopyConstructible。您(错误)调用std::bind()将创建副本。

此外,您似乎误解了引用是如何工作的 - auto& ref = b当然会创建对b的引用;但ref本身仍然是一个左值,因此将它传递给std::bind()并不会突然改变行为。

最后,考虑到你正在使用原子能,这是C++ 11,这意味着你还可以使用lambda表达式,这将允许你表达你的代码,而不必诉诸std::ref

std::function<void()> delegate = [&b] { b = true; }; 

但是要小心!在这两种情况下,无论你使用std::bind + std::ref或我的拉姆达上面的例子,你必须确保b仍然有效,直到你与delegate完成 - 以参考扩展原始对象的生命周期。