[英]How to generate a non-const method from a const method?
在努力保持正确性的同时,我经常发现自己正在编写这样的代码
class Bar;
class Foo {
public:
const Bar* bar() const { /* code that gets a Bar somewhere */ }
Bar* bar() {
return const_cast< Bar* >(
static_cast< const Foo* >(this)->bar());
}
};
对于很多方法,比如bar()
。 编写这些非const方法,手动调用常量方法是乏味的; 此外,我觉得我在重复自己 - 这让我心疼。
我该怎么做才能减轻这个任务? (不允许使用宏和代码生成器。)
编辑:除了litb的解决方案,我也喜欢我自己的解决方案。 :)
另一种方法是编写一个调用函数的模板(使用CRTP)并从中继承。
template<typename D>
struct const_forward {
protected:
// forbid deletion through a base-class ptr
~const_forward() { }
template<typename R, R const*(D::*pf)()const>
R *use_const() {
return const_cast<R *>( (static_cast<D const*>(this)->*pf)() );
}
template<typename R, R const&(D::*pf)()const>
R &use_const() {
return const_cast<R &>( (static_cast<D const*>(this)->*pf)() );
}
};
class Bar;
class Foo : public const_forward<Foo> {
public:
const Bar* bar() const { /* code that gets a Bar somewhere */ }
Bar* bar() { return use_const<Bar, &Foo::bar>(); }
};
请注意,调用没有丢失性能:由于成员指针作为模板参数传递,因此可以像往常一样内联调用。
使用以下技巧:
class Bar;
class Foo {
public:
Bar* bar() {
// non-const case
/* code that does something */
}
const Bar* bar() const {
return This().bar(); // use non-const case
}
private:
//trick: const method returns non-const reference
Foo & This() const { return const_cast<Foo &>(*this); }
};
注意,可以使用独特的功能, This
对于任何常量/非const功能。
没有static_cast的替代解决方案(但我更喜欢第一个):
class Bar;
class Foo {
public:
const Bar* bar() const { /* code that does something */ }
Bar* bar() { return const_cast<Bar*>(cthis().bar()); } // use const case
private:
const Foo & cthis() const { return *this; }
};
你可以这样做:
class Bar;
class Foo {
public:
const Bar* bar() const { return getBar(); }
Bar* bar() {
return getBar();
}
private:
Bar* getBar() const {/* Actual code */ return NULL;}
};
我个人的感觉是,如果你这么做,你的设计中有一些可疑的东西。 在我不得不做类似的事情的情况下,我通常通过方法可变来访问这个东西。
我之前也感受到了这种痛苦 - 本质上,你试图通过bar()
告诉编译器constness“传播”。 不幸的是,据我所知,没有办法自动执行此操作...您只需手动编写函数的第二个版本。
仅供参考 - OP发布的代码是Scott Meyers的“Effective C ++ - Third Edition”中的首选方法。 见第3项。
我弯下脑子后,自己想出了答案。 但是,我想我可以使用litb的回答中的想法来改进它,我将在稍后发布。 所以到目前为止,我的解决方案如下:
class ConstOverloadAdapter {
protected:
// methods returning pointers
template<
typename R,
typename T >
R* ConstOverload(
const R* (T::*mf)(void) const) {
return const_cast< R* >(
(static_cast< const T* >(this)->*mf)());
}
// and similar templates for methods with parameters
// and/or returning references or void
};
class Bar;
class Foo : public ConstOverloadAdapter {
public:
const Bar* bar() const {
/* implementation */ }
Bar* bar(void* = 0) { // the dummy void* is only needed for msvc
// since it cannot distinguish method overloads
// based on cv-type. Not needed for gcc or comeau.
return ConstOverload(&Foo::bar); }
};
假设你接受const-correctness作为一种技术,那么我认为这意味着你更喜欢编译器检查的const-correctness以简洁。 所以你希望编译器检查两件事:
如果const版本调用非const,则不会得到(2)。 如果非const版本调用const one并且const_casts结果,那么你不会得到(1)。 例如,假设Bar
实际上是char
,并且您编写的代码最终返回(在某些情况下)字符串文字。 这将编译(并且-Wwrite-strings不会发出警告),但是调用者最终会得到一个指向字符串文字的非const指针。 这与“您更喜欢编译器检查的const-correctness”相矛盾。
如果他们都调用辅助成员函数Bar *getBar() const
,那么你得到(1)和(2)。 但是如果可以编写那个辅助函数,那么为什么你首先要搞乱const和非const版本,那么修改const Foo返回的Bar是完全可以的? 有时候,实现的一些细节可能意味着你实现了一个带有两个访问器的接口,即使你只需要一个。 否则无法写入帮助程序,或者只能由单个帮助程序替换这两个函数。
只要代码大小不是问题,我认为实现(1)和(2)的最佳方法是让编译器实际考虑两种情况:
struct Bar { int a; };
struct Foo {
Bar *bar() { return getBar<Bar>(this); }
const Bar *bar() const { return getBar<const Bar>(this); }
Bar *bar2() const { return getBar<Bar>(this); } // doesn't compile. Good.
Bar *bar3() const { return getBar<const Bar>(this); } // likewise
private:
template <typename B, typename F>
static B *getBar(F *self) {
// non-trivial code, which can safely call other functions with
// const/non-const overloads, and we don't have to manually figure out
// whether it's safe to const_cast the result.
return &self->myBar;
}
Bar myBar;
};
如果代码是微不足道的,就像访问对象拥有的某个数组的operator[]
那么我只是复制代码。 在某些时候,上面的函数模板比复制更少编码工作,此时使用模板。
我认为const_cast方法虽然聪明且看似标准,但是没有用,因为它选择了简洁性而不是编译器检查的const-correctness。 如果方法中的代码很简单,那么您可以复制它。 如果它不是微不足道的,那么对于您或代码维护者来说,看到const_cast实际上是有效的并不容易。
你的代码暗示const bar()实际上正在创建并返回一个新的Bar,如果你做了那么多,我发现这很奇怪。
对我来说,更大的问题是成员函数中的常量仅限于引用和非指针成员。 你拥有(非常量)指针成员的那一刻,const函数可以改变它们,即使它声称是盒子上的常量。
如果您的Bar实例是成员变量,请考虑返回引用。
Bar的常数是否会影响你的Foo的常数? 或者你只是这样做,因为没有办法区分const Bar * vs Bar *版本? 如果是后一种情况,我只需要一个bar()返回一个Bar *并完成它。 无论如何,毕竟,需要一个const Bar *才能拿到Bar *就好了。 别忘了,bar()方法的常量是关于Foo,而不是关于Bar。
另外,如果你同时提供常量和非常量的Bar *并且很高兴让Foo成为非常量的,那么你的Foo的常量(以及bar()方法)显然并不那么重要,你只需填写const和非const版本就可以了。 在这种情况下,如果你允许非const版本,再次只提供/ only /那个,const的使用者将愉快地采用非const。 只是,你有const和非const去那里的方式,看起来它对程序没有太大的影响。
如果你想让const Foo对象能够返回const和非const的s,那么只需要使用bar()const并返回一个非const的Bar 。
我不知道,我必须看到更多才能给出真正的答案。
仔细思考,哪些常量是真正连接的,以及你的程序是否需要const Foos和非const Foos。 似乎你没有考虑到程序需要的所有可能性。
一张图片价值超过1万字,所以:
const Item*
Storage::SearchItemBySomething( const Something &something ) const
{
const Item* pData = NULL;
// Algorythm for searching.
return pData;
}
Item*
Storage::SearchItemBySomething( const Something &something )
{
return (Item*) ((const Storage*)this)->_SearchItemBySomething(something);
}
从实际代码复制和更改。 一般的想法是你实现你的const方法,你编写使用第一个方法的非const方法。 你需要将“this”指针强制转换为“const”,你需要从返回的“const Item *”中抛弃“const”。
您可以将C样式转换替换为C ++样式转换
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.