示例:
#include <cstdio>
class Dummy {};
template<typename T>
class IsDummy { public: enum { Value = 0, }; };
template<>
class IsDummy<Dummy> { public: enum { Value = 1, }; };
template<typename T, int isDummy = IsDummy<T>::Value>
class Wrapper { };
template<typename T>
class Wrapper<T, 1> {
public:
typedef T Value;
};
static void func(Dummy *) {}
static void func(const char *) {}
class Object0 {
public:
template<typename T>
operator T *(void) const {return NULL;}
};
class Object1 {
public:
template<typename T>
operator typename Wrapper<T>::Value *(void) const {return NULL;}
};
int main(int argc, char **argv)
{
Object0 o0;
func(o0); // <= ambiguous
Object1 o1;
func(o1); // <= no matching function
return 0;
}
我想做的事:
func(o0)
和 func(o1)
仅匹配 func(Dummy *)
(以及 IsDummy<>
专门化的任何其他类),但不适用于 func(const char *)
operator T()
函数))发生了什么:
func(o0)
会导致 ambiguous
func(o1)
会导致 no matching function
我认为两个问题:
Object0 o0
是一个对象而不是指针,您应该使用func(&o0)
,也适用于Object o1
。Object0*
不能自动/隐式转换为Dummy*
和const char&
,您应该指定func(Object0*)
或使Object0
从Dummy*
派生(将派生类转换为基类可能会丢失一些功能)。 class Object0: public Dummy {...}
func(&o0); // Object* -> Dummy* (candidate because convertible)
T
不能从 typename Wrapper<T>::Value
推出。在考虑模板化转换运算符时,如果无法推断出任何模板参数(从默认参数或目标类型),则无法调用转换运算符。
你必须使
T
可推论。为了避免转换为其他指针类型,请使用 SFINAE 禁用无效 T
的转换运算符。
在C++11中,你可以这样做:
class Object1 {
public:
template<typename T, typename std::enable_if<IsDummy<T>::Value>::type* = nullptr>
operator T *(void) const {return nullptr;}
};
但是,C++03 中不存在默认模板参数。您无法更改返回类型,因为
operator std::enable_if_t<IsDummy<T>::value, T*>
也是不可推论的。转换运算符也不接受非对象参数。
剩下一个位置:动态异常规范:
class Object1 {
public:
template<typename T>
operator T *(void) throw(typename std::enable_if<IsDummy<T>::Value, void*>::type) const {return NULL;}
};
(
std::enable_if
相对容易实现,但这也有一个缺点,现在你的操作员有一个动态异常规范)