c – 一个循环的const-connundrum

前端之家收集整理的这篇文章主要介绍了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对中,并将其显示标题中.这样,你几乎只有重复的功能签名.

猜你在找的C&C++相关文章