最近我一直在玩模板并偶然发现以下问题。我正在实现这样的CRTP模式:
template<typename derived_t>
struct protocol_object
{
...
};
struct data_object : public protocol_object<data_object>
{
...
};
我现在想在成员模板函数中匹配class protocol_object
的实例,同时仍然接受非CRTP类型:
struct consumer_impl
{
template<typename derived_t>
void match(protocol_object<derived_t> &value)
{
std::cout << "protocol_class";
};
template<typename T>
void match(T &value)
{
std::cout << "any other type";
};
}
不幸的是,只有第二个版本被调用。显然match(protocol_object<derived_t> &value)
不会被视为或拒绝使用更为通用的格式match(T &value)
。
data_object object;
double value;
consumer_impl consumer;
consumer.match(value); // yields "any other type" OK
consumer.match(object); // also yields "any other type" but want "protocol_class"
有没有办法解决这个问题?
感谢任何提示。 ·阿尔
答案 0 :(得分:2)
这与CRTP无关。这是以下的一般情况:
问题是T& value
与Derived&
完全匹配,而Base&
是不精确的匹配。因此,我们将使一般形式更糟糕:
struct conversion_required { conversion_required(int) {} };
template<typename derived_t>
void match_impl(protocol_object<derived_t> &value, int)
{
std::cout << "protocol_class";
};
template<typename T>
void match_impl(T &value, conversion_required)
{
std::cout << "any other type";
};
template<typename T>
void match(T& value)
{
return match_impl(value, 0);
}
现在需要转发的专业化比一般模板更好,需要用户定义的转换。
答案 1 :(得分:0)
重载决策是基于静态类型执行的,因为它是编译时编译器的决定。试试这个:
consumer.match(static_cast<protocol_object<data_object>&>(object));
答案 2 :(得分:0)
第二个函数是更好的匹配,因为它不需要转换,而第一个函数需要派生到基础的转换。
你可以使用boost来克服这个问题:
template <class T>
void
match (typename boost::enable_if_c
<boost::is_base_of<protocol_object<T>,T>::value, T>::type& t)
{
std::cout << "protocol_class";
}
template <class T>
void
match (typename boost::disable_if_c
<boost::is_base_of<protocol_object<T>,T>::value, T>::type& t)
{
std::cout << "any other type";
}
这适用于从T
派生的所有课程protocol_object<T>
,但不适用于protocol_object<T>
本身。您可以为它添加另一个重载(基本上,重用您的第一个match
函数),或修改enable_if
中的条件,使其与protocol_object<T>
匹配。