c – 一个循环的const-connundrum
我经常发现自己必须定义一个函数的两个版本,以便有一个是const,一个是非const(通常是一个getter,但不总是).两者之间的差异仅在于一个的输入和输出是const,而另一个的输入和输出是非const.功能的内在功能 – 真正的工作就是IDENTICAL.
然而,为了正确性,我需要它们.作为一个简单的实际例子,请执行以下操作: inline const ITEMIDLIST * GetNextItem(const ITEMIDLIST * pidl) { return pidl ? reinterpret_cast<const ITEMIDLIST *>(reinterpret_cast<const BYTE *>(pidl) + pidl->mkid.cb) : NULL; } inline ITEMIDLIST * GetNextItem(ITEMIDLIST * pidl) { return pidl ? reinterpret_cast<ITEMIDLIST *>(reinterpret_cast<BYTE *>(pidl) + pidl->mkid.cb) : NULL; } 你可以看到,他们做同样的事情.我可以选择使用更多的演员来定义一个,如果勇气 – 实际工作不那么简单,这更合适: inline const ITEMIDLIST * GetNextItem(const ITEMIDLIST * pidl) { return pidl ? reinterpret_cast<const ITEMIDLIST *>(reinterpret_cast<const BYTE *>(pidl) + pidl->mkid.cb) : NULL; } inline ITEMIDLIST * GetNextItem(ITEMIDLIST * pidl) { return const_cast<ITEMIDLIST *>(GetNextItem(const_cast<const ITEMIDLIST *>(pidl)); } 所以,我觉得这是一个非常繁琐和冗余的事情.但是,如果我想编写常量正确的代码,那么我要么提供上述两种,要么我必须将我的“消费者代码”与常量丢弃,以解决仅定义一个或另一个的问题. 有更好的模式吗?你认为这个问题的最佳方法是什么? >提供给定函数的两个副本 – const和非const版本 还是有更好的方法来完成这个问题? 奖励积分: >你发现这是C const系统的一个不幸的副产品 编辑: 如果我只提供第一个 – const的const返回const,那么任何需要修改返回的项目的消费者,或将返回的项目交给另一个将修改它的函数,必须抛弃const. 类似地,如果我只提供第二个定义 – 使用非const并返回非const,那么具有const pidl的消费者必须抛弃const,才能使用上述的功能,老实说,它不会修改const的项目本身. 也许更抽象是可取的: THING & Foo(THING & it); const THING & Foo(const THING & it); 我希望有一个结构: const_neutral THING & Foo(const_neutral THING & it); 我当然可以做一些像: THING & Foo(const THING & it); 但是这总是让我错误的方式.我在说“我不修改你的内容,但是我要摆脱你在代码中默默地委托我的这个常识.” 现在,客户端有: const THING & it = GetAConstThing(); ... ModifyAThing(Foo(it)); 这只是错误GetAConstThing与调用者的合同是给它一个const引用.调用者预期不要修改该事物 – 只能使用常量操作.是的,调用者可能是邪恶的,并抛弃它的一般,但这只是邪恶(tm). 对我来说,这个问题的症结在于,Foo是中立的.它实际上并不修改它给定的东西,但是它的输出需要传播其参数的一致性. 注意:编辑第二次进行格式化. 解决方法
我不认为这是常量正确性的缺陷本身,而是缺乏方便的能力来概括cv-qualifiers的方法(以同样的方式,我们可以通过模板来推广类型).假设,如果你能写下如下:
template<cvqual CV> inline CV ITEMIDLIST* GetNextItem(CV ITEMIDLIST * pidl) { return pidl ? reinterpret_cast<CV ITEMIDLIST *>(reinterpret_cast<CV BYTE *>(pidl) + pidl->mkid.cb) : NULL; } ITEMIDLIST o; const ITEMIDLIST co; ITEMIDLIST* po = GetNextItem(&o); // CV is deduced to be nothing ITEMIDLIST* pco = GetNextItem(&co); // CV is deduced to be "const" 现在你可以用模板元编程来做这样的事情,但是这样做 template<class T,class TProto> struct make_same_cv_as { typedef T result; }; template<class T,class TProto> struct make_same_cv_as<T,const TProto> { typedef const T result; }; template<class T,volatile TProto> { typedef volatile T result; }; template<class T,const volatile TProto> { typedef const volatile T result; }; template<class CV_ITEMIDLIST> inline CV_ITEMIDLIST* GetNextItem(CV_ITEMIDLIST* pidl) { return pidl ? reinterpret_cast<CV_ITEMIDLIST*>(reinterpret_cast<typename make_same_cv_as<BYTE,CV_ITEMIDLIST>::result*>(pidl) + pidl->mkid.cb) : NULL; } 上述问题是所有模板的常见问题 – 只要具有正确名称的成员,而不仅仅是ITEMIDLIST,它将让您传递任何随机类型的对象.当然,你可以使用各种“静态断言”实现,但这也是一个黑客. 或者,您可以使用模板版本重新使用.cpp文件中的代码,然后将其包装到一个const / non-const对中,并将其显示在标题中.这样,你几乎只有重复的功能签名. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |