模板常量/非常量方法
template const / non const method
假设我们有这样的代码:
template<class CALLBACK>
struct INIFile{
INIFile(CALLBACK &processor) :
processor(processor ){}
bool process(){
// lots of code here,
// call processor
processor(123);
return true;
}
CALLBACK &processor;
};
struct MyProcessor{
void operator()(int val){
// do something
}
};
struct MyConstProcessor{
void operator()(int val) const{ // !!!!!
// do something
}
};
int main(){
MyProcessor p;
INIFile<MyProcessor> ini(p);
ini.process();
// const case:
MyConstProcessor cp;
INIFile<MyConstProcessor> cini(cp);
cini.process();
}
在这两种情况下,INIFile<>::process()
将是一个非 const
成员函数。
如果 CALLBACK::operator()
是 const
,是否有一种简单的方法使 process()
成为 const
成员函数,而无需重复 INIFile<>::process()
中的所有逻辑?
另一种我找到但不太喜欢的方法是使用指针而不是引用。
这是代码。
请注意,在这种特殊情况下,我们根本不需要检查 nullptr
。
template<class CALLBACK>
struct INIFile{
INIFile(CALLBACK &processor) :
processor(& processor ){}
bool process() const{
// lots of code here,
// call processor
processor->operator()(123);
return true;
}
CALLBACK *processor;
};
您的问题已通过执行以下操作得到解决:
template<class CALLBACK>
struct INIFile{
INIFile(CALLBACK &processor) :
processor(processor){}
template <class T>
bool process_impl(T& processor) const {
// deliberately shadow processor
// reduce likelihood of using member processor, but can use other member variables
// lots of code
processor(123);
return true;
}
bool process() const {
return process_impl(const_cast<const CALLBACK&>(processor));
}
bool process() {
return process_impl(processor);
}
CALLBACK& processor;
};
这当然在技术上超载了 process
,但它具有您想要的完全相同的效果。如果 processor
的调用运算符 未 标记为 const,并且您尝试通过对象的 const 引用或 const 副本调用 process
,您会得到编译错误(与您的解决方案不同)。那是因为 process
的 const
重载被调用,它将 const
添加到传递的处理器,然后处理器上的调用运算符当然会失败。
但是,如果回调确实提供了一个 const
调用运算符,那么任何一个进程调用都会做完全相同的事情。这实际上意味着您可以在 INIFile
的 const 副本上调用 process
,这相当于 process
是 const
.
如果回调还重载调用运算符,则此实现将转发到正确的那个,但您没有将其指定为条件。唯一需要注意的是,process_impl
永远不应该访问成员变量 processor
,因为该成员变量将始终是可变的,即即使不应该调用也会工作(如你的解决方案)。我故意遮挡以防止这种情况发生。这不是那么漂亮,但作为一个实现细节,它还不错,而且它确实删除了重复。
假设我们有这样的代码:
template<class CALLBACK>
struct INIFile{
INIFile(CALLBACK &processor) :
processor(processor ){}
bool process(){
// lots of code here,
// call processor
processor(123);
return true;
}
CALLBACK &processor;
};
struct MyProcessor{
void operator()(int val){
// do something
}
};
struct MyConstProcessor{
void operator()(int val) const{ // !!!!!
// do something
}
};
int main(){
MyProcessor p;
INIFile<MyProcessor> ini(p);
ini.process();
// const case:
MyConstProcessor cp;
INIFile<MyConstProcessor> cini(cp);
cini.process();
}
在这两种情况下,INIFile<>::process()
将是一个非 const
成员函数。
如果 CALLBACK::operator()
是 const
,是否有一种简单的方法使 process()
成为 const
成员函数,而无需重复 INIFile<>::process()
中的所有逻辑?
另一种我找到但不太喜欢的方法是使用指针而不是引用。
这是代码。
请注意,在这种特殊情况下,我们根本不需要检查 nullptr
。
template<class CALLBACK>
struct INIFile{
INIFile(CALLBACK &processor) :
processor(& processor ){}
bool process() const{
// lots of code here,
// call processor
processor->operator()(123);
return true;
}
CALLBACK *processor;
};
您的问题已通过执行以下操作得到解决:
template<class CALLBACK>
struct INIFile{
INIFile(CALLBACK &processor) :
processor(processor){}
template <class T>
bool process_impl(T& processor) const {
// deliberately shadow processor
// reduce likelihood of using member processor, but can use other member variables
// lots of code
processor(123);
return true;
}
bool process() const {
return process_impl(const_cast<const CALLBACK&>(processor));
}
bool process() {
return process_impl(processor);
}
CALLBACK& processor;
};
这当然在技术上超载了 process
,但它具有您想要的完全相同的效果。如果 processor
的调用运算符 未 标记为 const,并且您尝试通过对象的 const 引用或 const 副本调用 process
,您会得到编译错误(与您的解决方案不同)。那是因为 process
的 const
重载被调用,它将 const
添加到传递的处理器,然后处理器上的调用运算符当然会失败。
但是,如果回调确实提供了一个 const
调用运算符,那么任何一个进程调用都会做完全相同的事情。这实际上意味着您可以在 INIFile
的 const 副本上调用 process
,这相当于 process
是 const
.
如果回调还重载调用运算符,则此实现将转发到正确的那个,但您没有将其指定为条件。唯一需要注意的是,process_impl
永远不应该访问成员变量 processor
,因为该成员变量将始终是可变的,即即使不应该调用也会工作(如你的解决方案)。我故意遮挡以防止这种情况发生。这不是那么漂亮,但作为一个实现细节,它还不错,而且它确实删除了重复。