2017-04-08 46 views
2

我正在使用C++ 11,并试图在我的应用程序中设置一个通用的Handle类,其中有时可能使用不同的基础类型转换句柄,但仅限于如果底层类型与祖先/后代相关,否则尝试进行转换应该简单地失败。我还需要一个永不失败的函数,告诉我两种类型之间是否可以进行转换。特别是,我不希望基础类型尝试对不在其自己的祖先/后代行中的类型进行任何转换,所以我想如果我在编译时告诉我的布尔值定义了模板仿函数,类型是相关的,并且使用模板专业化来拒绝转换(如果它们不相关),或者如果转换请求相关,则将转换请求转发给基础类型。每个基类都包含一个模板化转换函数,该函数知道如何转换为其中的每个相应类型以及模板布尔函数,该函数根据类实例的内部状态指示是否可以进行此类转换。在编译时选择使用模板调用哪个函数

我放在一起看起来是这样的:

template<class T> 
class MyHandle { 
public: 
    ... 
    template<bool> struct can_be_ref {  
     template<class U> bool operator()(const MyHandle *, const U*) const 
     { 
     } 
    }; 

    template<bool> struct as_ref { 
     template<class U> MyHandle<U> operator()(const MyHandle *, const U*) const 
     { 
      throw std::runtime_error("Illegal type conversion"); 
     } 
    }; 
    template<class U> bool can_be(); 
    template<class U> MyHandle<U> as(); 
private: 
    const T* get_member_reference() const; 
}; 

template<class T> struct MyHandle<T>::can_be_ref<true> {  
    template<class U> bool operator()(const MyHandle<T> *ptr, const U*) 
    { 
     ptr->get_member_reference()->can_be<U>(); 
    } 
}; 

template<class T> struct MyHandle<T>::as_ref<true> {  
    template<class U> MyHandle<U> operator()(const MyHandle<T> *ptr, const U*) const 
    { 
     return ptr->get_member_reference()->as<U>(); 
    } 
}; 

template<class T> template<class U> bool MyHandle<T>::can_be() 
{ 
    return can_be_ref < std::is_base_of<T, U>::value || std::is_base_of<U, T>::value >()(this, reinterpret_cast<const U *> (nullptr)); 
} 

template<class T> template<class U> MyHandle<U> MyHandle<T>::as() 
{ 
    return as_ref < std::is_base_of<T, U>::value || std::is_base_of<U, T>::value >()(this, reinterpret_cast<const U *> (nullptr)); 
} 

这并不编译,但是,我不知道我做错了。失败发生在我尝试专门化can_be_refas_ref结构体,其中编译器抱怨太少的模板参数列表。

希望我想要做的事情在我提供的解释和遗憾的代码片段之间清楚无效,但它是我能想到的唯一方法来描述我想要做的事情。我究竟做错了什么?

编辑:

澄清,说我有下面的类层次结构:

class A { 
public: 
    template<class U> bool can_be(); 
    template<class U> MyHandle<U> as(); 
... 
}; 

class B : public A{ 
... 
}; 

class C { 
public: 
    template<class U> bool can_be(); 
    template<class U> MyHandle<U> as(); 
... 
}; 

每个层级定义了一个can_beas方法只关注如何在其层次结构中的项目,并在如果模板的参数不是正确的类型,在某些情况下可能会导致编译器错误,这就是编译时必须检查该类型的原因。
而假设我们有以下变量定义:

MyHandle<A> a; 
MyHandle<B> b; 
MyHandle<C> c; 

因为ab是相关类型的,A::can_beA::as它们之间可以自由使用,但是A :: can_be可能会产生一个编译错误。因此,MyHandle中的包装器将其隐藏起来,例如MyHandle<A>::can_be<C>()就会返回false。虽然MyHandle<B>::as<C>()总是会引发异常,甚至不会尝试生成对B::as<C>的调用,因为这可能会导致编译错误。

编辑:下面

每卡米尔的建议,解决办法是migate模板定义到周围的类。我所做的就是创建一个辅助模板如下:

template<class T,class U,bool> class MyHandleConverter 
{ 
public: 
    inline MyHandleConverter(const MyHandle<T> *) { } 
    inline bool can_be() const { return false; } 
    inline MyHandle<U> as() const { return MyHandle<U>(nullptr); } 
}; 

我决定放弃投掷无效转换例外,现在MyHandle的每个实例包含一个名为value一个空指针,可以包含一个指向更多信息实际的基础类型,它是nullptr如果它是无效的,所以我可以再创建为MyHandleConverterClass部分专业化如下:

template<class T,class U> class MyHandleConverter<T,U,true> { 
public: 
    inline MyHandleConverter(const MyHandle<T> *ref):reference(ref) { }  
    inline bool can_be() const { 
     if (std::is_base_of<T,U>::value) { 
      return true; 
     } else if (reference->value == nullptr) { 
      return false; 
     } else { 
      return reference->underlying_can_be((const U*)(nullptr)); 
     } 
    } 
    inline MyHandle<U> as() const { 
     if (std::is_base_of<U,T>::value) { 
      return MyHandle<U>(reference->value); 
     } else if (reference->value == nullptr) { 
      return MyHandle<U>(nullptr); 
     } else { 
      return reference->underlying_as((const U*)(nullptr)); 
     } 
    } 
private: 
    const MyHandle<T> *reference;  
}; 

而是像我一样以前抛出异常的,我不是返回一个无效MyHandle (它有一个特殊的构造函数,MyHandle(nullptr_t)MyHandle的状态可以通过一个简单的布尔值is_valid()方法来查询(如果需要,调用者可以选择抛出一个异常,这对于我的目的而言,不得不编写更少的try .... catch块比如果我有as<U>函数本身在失败时抛出异常)。

的MyHandle类有一个模板underlying_can_be方法和模板underlying_as方法,该方法简单地转发它们分别请求到下面的类类型的can_beas或方法。值得一提的是,这些方法甚至不会编译器生成的,如果它没有被通过MyHandleConverter<T,U,true>类调​​用,所以现在的MyHandle can_beas方法是这样写的:

template <class T> template<class U> bool MyHandle<T>::can_be() const { 
    return MyHandleConverter<T, U, are_related_handle_types<U,T>()>(this).can_be(); 
} 

template<class T> template<class U> MyHandle<U> MyHandle<T>::as() const { 
    return MyHandleConverter<T, U, are_handle_types_related<U,T>()>(this).as(); 
} 

其中are_handle_types_related是一个模板constexpr函数,如果调用底层模板类型时发现该函数关系紧密,那么调用MyHandle的can_behas方法的底层类型不会导致编译器错误,或者在某些情况下会导致无法在编译时检测到的逻辑错误时间,甚至在运行时不需要在每个基础类型中编写复杂的检测逻辑和can_be方法,只需通过检测这两个类是从适当的类型派生出来,以便转换过程合理地成功。

这样一来,当类型不兼容的are_handle_types_related检测到,这将是无效的调用相应类型的can_beas方法,所创建的MyHandleConverter实例为MyHandleConverter<T,U,false>不尝试调用底层类类型,而MyHandleConverter<T,U,true>确实,但只会实例化已经发现可以接受调用底层类型的适当转换函数的类。

+0

您希望如何使用他们目前尚不清楚给我。什么是一些有效的用法?什么是一些无效的用法?发布它们将非常有用。 –

回答

2

专注你有专业化前增加template关键字模板,如:

template<class T> // Template parameter for 'MyHandle<T>' 
template<> // No unspecialized template parameters for 'can_be_ref', but indicate that it is a template anyway 
struct MyHandle<T>::can_be_ref<true> 
{  
    template<class U> bool operator()(const MyHandle<T> *ptr, const U*) 
    { 
     ptr->get_member_reference()->can_be<U>(); 
    } 
}; 

然而,这并不编译任。根据http://en.cppreference.com/w/cpp/language/template_specialization

成员或成员模板可嵌套在许多封闭类 模板中。对于这样一个成员的显式专门化,对于每个专门显式为 的封闭类模板,都有一个 模板<>。在这样一个嵌套的声明,一些水平可能 保持非特

因此,我们不能完全专注模板没有(不同之处在于,如果它包围类是非特不能专注一类成员 模板)专门研究MyHandle。解决方案可能是模板参数偏特 - 移动参数Ucan_be_ref::operator()can_be_ref级别:

template<class T> 
class MyHandle 
{ 
public: 
... 
    template<class U, bool> 
    struct can_be_ref 
    { 
     bool operator()(const MyHandle<T> *ptr, const U*) const 
     { 
      return false; 
     } 
    }; 

    template<class U, bool> 
    struct as_ref 
    { 
     MyHandle<U> operator()(const MyHandle<T> *, const U*) const 
     { 
      throw std::runtime_error("Illegal type conversion"); 
     } 
    }; 
... 
}; 

然后我们可以去一个偏特:

template<class T> 
template<class U> 
struct MyHandle<T>::can_be_ref<U, true> 
{ 
    bool operator()(const MyHandle<T> * ptr, const U*) const 
    { 
     return ptr->get_member_reference()->can_be<U>(); 
    } 
}; 

template<class T> 
template<class U> 
struct MyHandle<T>::as_ref<U, true> 
{ 
    MyHandle<U> operator()(const MyHandle<T> *ptr, const U*) const 
    { 
     return ptr->get_member_reference()->as<U>(); 
    } 
}; 

template<class T> 
template<class U> bool MyHandle<T>::can_be() const 
{ 
    return can_be_ref<U, 
      std::is_base_of<T, U>::value || std::is_base_of<U, T>::value>()(
      this, nullptr); 
} 

template<class T> 
template<class U> MyHandle<U> MyHandle<T>::as() 
{ 
    return as_ref<U, 
      std::is_base_of<T, U>::value || std::is_base_of<U, T>::value>()(
      this, nullptr); 
} 

其实,当我遵守它例如类AB,C遵从投诉在线return ptr->get_member_reference()->can_be<U>();即:expected primary-expression before ')' token。我真的不知道这里有什么问题。称它为get_member_reference()->A::can_be<U>()工作。得到成功的解决方法是通过传递U类型的参数来确定can_be<U>()U参数:

class A { 
public: 
    template<class U> bool can_be(const U*) 
{ 
return can_be<U>(); 
} 
    template<class U> MyHandle<U> as(const U*) 
{ 
return as<U>(); 
} 

    template<class U> bool can_be(); 
    template<class U> MyHandle<U> as(); 
}; 

template<class T> 
    template<class U> 
    struct MyHandle<T>::can_be_ref<U, true> 
    { 
     bool operator()(const MyHandle<T> * ptr, const U* uptr) const 
     { 
      return ptr->get_member_reference()->can_be(uptr); 
     } 
    }; 

    template<class T> 
    template<class U> 
    struct MyHandle<T>::as_ref<U, true> 
    { 
     MyHandle<U> operator()(const MyHandle<T> *ptr, const U* uptr) const 
     { 
      return ptr->get_member_reference()->as(uptr); 
     } 
    };