我有一些管理异常安全的原始代码,如下所示:
void foo() {
HDC hdc = //get an HDC
HBITMAP hbitmap = //get an HBITMAP
HGDIOBJ hbitmapOld = SelectObject(hdc, hbitmap);
try {
//do something that may throw an exception
} catch (...) {
SelectObject(hdc, hbitmapOld);
throw;
}
}现在我想去掉try块,使用unique_ptr自动选择旧的位图。所以我写了这样的东西:
void foo() {
//...
//HGDIOBJ is defined as void*
std::unique_ptr<void, std::function<HGDIOBJ(HGDIOBJ)>>
hbitmapOld(SelectObject(hdc, hbitmap), std::bind(SelectObject, hdc, _1));
}但是它不能编译。如何让它变得正确?
错误消息:
1>c:\program files (x86)\microsoft visual studio 10.0\vc\include\xxcallfun(7): error C2664: 'HGDIOBJ (HDC,HGDIOBJ)' : cannot convert parameter 2 from 'boost::arg<I>' to 'HGDIOBJ'
1> with
1> [
1> I=1
1> ]
1> No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
1> c:\program files (x86)\microsoft visual studio 10.0\vc\include\xxbind1(292) : see reference to function template instantiation '_Ret std::tr1::_Callable_fun<_Ty,_Indirect>::_ApplyX<_Ret,_Arg&,boost::arg<I>&>(_Arg0,_Arg1) const' being compiled
1> with
1> [
1> _Ret=_Rx,
1> _Ty=HGDIOBJ (__stdcall *const )(HDC,HGDIOBJ),
1> _Indirect=false,
1> _Arg=HDC,
1> I=1,
1> _Arg0=HDC &,
1> _Arg1=boost::arg<1> &
1> ]
1> c:\program files (x86)\microsoft visual studio 10.0\vc\include\xxbind0(31) : see reference to function template instantiation '_Ret std::tr1::_Bind2<_Callable,_Arg0,_Arg1>::_ApplyX<_Rx,void&,std::tr1::_Nil&,std::tr1::_Nil&,std::tr1::_Nil&,std::tr1::_Nil&,std::tr1::_Nil&,std::tr1::_Nil&,std::tr1::_Nil&,std::tr1::_Nil&,std::tr1::_Nil&>(_Barg0,_Barg1,_Barg2,_Barg3,_Barg4,_Barg5,_Barg6,_Barg7,_Barg8,_Barg9)' being compiled
1> with
1> [
1> _Ret=_Rx,
1> _Callable=std::tr1::_Callable_fun<HGDIOBJ (__stdcall *const )(HDC,HGDIOBJ),false>,
1> _Arg0=HDC,
1> _Arg1=boost::arg<1>,
1> _Barg0=HGDIOBJ &,
1> _Barg1=std::tr1::_Nil &,
1> _Barg2=std::tr1::_Nil &,
1> _Barg3=std::tr1::_Nil &,
1> _Barg4=std::tr1::_Nil &,
1> _Barg5=std::tr1::_Nil &,
1> _Barg6=std::tr1::_Nil &,
1> _Barg7=std::tr1::_Nil &,
1> _Barg8=std::tr1::_Nil &,
1> _Barg9=std::tr1::_Nil &
1> ]
1> c:\program files (x86)\microsoft visual studio 10.0\vc\include\xxcallobj(13) : see reference to function template instantiation 'void *std::tr1::_Bind_base<_Ret,_BindN>::operator ()<_Arg0&>(_Carg0)' being compiled
1> with
1> [
1> _Ret=HGDIOBJ ,
1> _BindN=std::tr1::_Bind2<std::tr1::_Callable_fun<HGDIOBJ (__stdcall *const )(HDC,HGDIOBJ),false>,HDC,boost::arg<1>>,
1> _Arg0=HGDIOBJ,
1> _Carg0=HGDIOBJ &
1> ]
1> c:\program files (x86)\microsoft visual studio 10.0\vc\include\xxfunction(65) : see reference to function template instantiation '_Ret std::tr1::_Callable_obj<_Ty>::_ApplyX<_Rx,_Arg0&>(void)' being compiled
1> with
1> [
1> _Ret=HGDIOBJ,
1> _Ty=std::tr1::_Bind<HGDIOBJ,HGDIOBJ,std::tr1::_Bind2<std::tr1::_Callable_fun<HGDIOBJ (__stdcall *const )(HDC,HGDIOBJ),false>,HDC,boost::arg<1>>>,
1> _Rx=HGDIOBJ,
1> _Arg0=HGDIOBJ
1> ]
1> c:\program files (x86)\microsoft visual studio 10.0\vc\include\xxfunction(64) : while compiling class template member function 'HGDIOBJ std::tr1::_Impl_no_alloc1<_Callable,_Rx,_Arg0>::_Do_call(_Arg0)'
1> with
1> [
1> _Callable=_MyWrapper,
1> _Rx=HGDIOBJ ,
1> _Arg0=HGDIOBJ
1> ]
1> c:\program files (x86)\microsoft visual studio 10.0\vc\include\xxfunction(386) : see reference to class template instantiation 'std::tr1::_Impl_no_alloc1<_Callable,_Rx,_Arg0>' being compiled
1> with
1> [
1> _Callable=_MyWrapper,
1> _Rx=HGDIOBJ ,
1> _Arg0=HGDIOBJ
1> ]
1> c:\program files (x86)\microsoft visual studio 10.0\vc\include\xxfunction(369) : see reference to function template instantiation 'void std::tr1::_Function_impl1<_Ret,_Arg0>::_Reset0o<_Myimpl,_Fty,std::allocator<_Ty>>(_Fty,_Alloc)' being compiled
1> with
1> [
1> _Ret=HGDIOBJ ,
1> _Arg0=HGDIOBJ ,
1> _Fty=std::tr1::_Bind<HGDIOBJ,HGDIOBJ,std::tr1::_Bind2<std::tr1::_Callable_fun<HGDIOBJ (__stdcall *const )(HDC,HGDIOBJ),false>,HDC,boost::arg<1>>>,
1> _Ty=std::tr1::_Function_impl1<HGDIOBJ ,HGDIOBJ >,
1> _Alloc=std::allocator<std::tr1::_Function_impl1<HGDIOBJ ,HGDIOBJ >>
1> ]
1> c:\program files (x86)\microsoft visual studio 10.0\vc\include\functional(113) : see reference to function template instantiation 'void std::tr1::_Function_impl1<_Ret,_Arg0>::_Reset<_Fx>(_Fty)' being compiled
1> with
1> [
1> _Ret=HGDIOBJ ,
1> _Arg0=HGDIOBJ ,
1> _Fx=std::tr1::_Bind<HGDIOBJ,HGDIOBJ,std::tr1::_Bind2<std::tr1::_Callable_fun<HGDIOBJ (__stdcall *const )(HDC,HGDIOBJ),false>,HDC,boost::arg<1>>>,
1> _Fty=std::tr1::_Bind<HGDIOBJ,HGDIOBJ,std::tr1::_Bind2<std::tr1::_Callable_fun<HGDIOBJ (__stdcall *const )(HDC,HGDIOBJ),false>,HDC,boost::arg<1>>>
1> ]
1> r:\programming\windows\biota\library\orchid\src\pixmap.cpp(182) : see reference to function template instantiation 'std::tr1::function<_Fty>::function<std::tr1::_Bind<_Result_type,_Ret,_BindN>>(_Fx)' being compiled
1> with
1> [
1> _Fty=HGDIOBJ (HGDIOBJ),
1> _Result_type=HGDIOBJ,
1> _Ret=HGDIOBJ,
1> _BindN=std::tr1::_Bind2<std::tr1::_Callable_fun<HGDIOBJ (__stdcall *const )(HDC,HGDIOBJ),false>,HDC,boost::arg<1>>,
1> _Fx=std::tr1::_Bind<HGDIOBJ,HGDIOBJ,std::tr1::_Bind2<std::tr1::_Callable_fun<HGDIOBJ (__stdcall *const )(HDC,HGDIOBJ),false>,HDC,boost::arg<1>>>
1> ]
1>c:\program files (x86)\microsoft visual studio 10.0\vc\include\xxcallfun(7): error C2664: 'HGDIOBJ (HDC,HGDIOBJ)' : cannot convert parameter 1 from 'boost::arg<I>' to 'HDC'
1> with
1> [
1> I=1
1> ]
1> No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
1>
1>Build FAILED.发布于 2010-09-02 21:34:43
这里您需要的是作用域保护习惯用法:http://www.drdobbs.com/cpp/184403758
发布于 2010-09-02 21:26:50
我认为一个更好的解决方案是编写一个小类,它将在构造函数中完成工作,并在析构函数中执行回滚。当堆栈在异常期间展开时,总是为堆栈类调用析构函数。我认为即使你让你的unique_ptr代码正常工作,这也是一个比这更笨拙的解决方案。
例如,在我的一些代码中,我有一个scoped_noredraw类,它可以防止窗口在更新时刷新。如果函数以正常或异常方式返回,则始终在析构函数中重新打开窗口刷新。
发布于 2010-09-02 23:46:01
谢谢你,AshleysBrain和Nemanja Trifunovic,他们提出了一种范围保护方法。
在我看来,unique_ptr是scope guard的一个更通用的实现(它应该能够做scope guard所做的事情,甚至更多),而且我的方法的逻辑是正确的,所以在理论上它应该可以工作。
经过一些测试,我终于明白为什么它不能工作了。这是因为SelectObject()使用__stdcall约定,而微软在编写std::bind时选择忽略这种不方便的行为。:(
https://stackoverflow.com/questions/3627363
复制相似问题