然而,为了正确性,我需要它们.作为一个简单的实际例子,请执行以下操作:
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是中立的.它实际上并不修改它给定的东西,但是它的输出需要传播其参数的一致性.
注意:编辑第二次进行格式化.
解决方法
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对中,并将其显示在标题中.这样,你几乎只有重复的功能签名.