|
假设有个类别widget;我们希望能够针对该类别做有效性测试,通常我们可能会为widget定义一个is_valid()成员函数
widget w;
if (w.is_valid()) {...}
然而有时候我们可能更希望接口更直接点,比如:
error e;
if (e) {...}
这时候可以采取的一个方法是提供一个转型:
比如转至一个int
class error {
public:
error (int code) : m_error_code(code) {}
operator int() const {return m_error_code; }
private:
int m_error_code;
};
通常我们并不鼓励这么做,自动转型暴露了内部数据,总是存在难以发现的隐患(我们应该尽可能遵循伟大的c_str()传统);
另外一种转型operator bool也不被鼓励
class error {
...
operator bool() const { return m_error_code != 0; }
};
原因是你有可能写出:
error e(1), e2(2);
if (e == e2) { ... }
在没有定义operator==的情况下,if (e == e2) 居然也可以通过,而且返回真。原因是e和e2都转型为bool后再做比较。
当然你可以定义operator==以消除这种状况的发生。
class error {
...
operator bool() const { return m_error_code != 0; }
bool operator==(const error& e) { return m_error_code == e.m_error_code; }
};
这时if (e == e2) { ... } 可以返回正确的结果,同时你也达到进行有效性测试的目的。
对于像smart_ptr这样的模板类别,情况会有点微妙的变化。你仍然可以进行有效性测试:
smart_ptr<int> s;
if (s) { ... }
但operator==却行为异常。因为你可能对着两个不同类型的smart_ptr进行比较,而对于不同类型的smart_ptr,并未有operator==被实作出来(你只会针对同型的smart_ptr实作operator==):
smart_ptr<A> s1;
smart_ptr<B> s2;
if (s1 == s2) {... }
结果是显而易见的,s1,s2分别实施转型至bool,if比较返回true
正确的做法是基于不同的smart_ptr类型产生不同的可测试有效性的类型:下面是一个方法(MordenC++p178)
template <class T>
class smart_ptr {
class nested_unspecified_type {
void opeartor delete(void*);
};
public:
opeartor nested_unspecified_type*() const {
if (!m_pointee) return 0;
static nested_unspecified_type n;
return &n;
}
};
nested_unspecified_type会声明而未定义void opeartor delete(void*); 的原因是阻止写出如下的delete代码:
delete smart_ptr<int>(new int);
另一个更加精妙的方法来自boost\system\error_code.cpp,即转型至一个函数指针
template <class T>
class smart_ptr {
typedef void (nested_unspecified_type)();
static void nested_unspecified_type_(){}
public:
opeartor nested_unspecified_type() const {
if (!m_pointee) return 0;
return nested_unspecified_type_;
}
};
延伸讨论:
上述除了opeartor==的行为需要考虑以外,其他的比较操作符也需要考虑,比如opeator!=, operator<等。如果没有定义这些操作符,那么smart_ptr或者error都会转型以完成比较,所以行为也就会未定义。
结论是:要定义有效性测试符,请采用转型为内部函数指针的转型符,同时也必须定义各种比较操作符 |
|