匹配派生类型而不是基类的模板方法
template method matching derived type instead of base
我有一组运算符,我需要为表达式模板覆盖这些运算符。我希望基类型的所有派生 类 都与基类型匹配。然后其他的东西会被通用类型捕获。不幸的是,泛型类型先于基类型获取派生类型。为了使事情变得美好和混乱,一切都被大量模板化,包括一些 CRTP。让我试着给出一个更简单的代码版本:
// Note: 'R' is used for return type
template <typename DerivedType, typename R>
class Base
{ // ...
};
template <typename E1, typename E2, typename R>
class MultOperation : public Base<MultOperation<E1, E2, R>, R>
{ // ...
};
template <typename T>
class Terminal : public Base<Terminal<T>, T>
{ // ...
};
// The broken operators:
template <typename T1, typename T2, typename R1, typename R2>
MultOperation<Base<T1, R1>, Base<T2, R2>, typename boost::common_type<R1, R2>::type>
operator*( Base<T1, R1> const& u, Base<T2, R2> const& v)
{
return MultOperation<Base<T1, R1>, Base<T2, R2>, typename boost::common_type<R1, R2>::type>(u, v);
}
template <typename T1, typename T2, typename R1, typename R2>
MultOperation<Terminal<T1>, Base<T2, R2>, typename boost::common_type<T1, R2>::type>
operator*( T1 const& u, Base<T2, R2> const& v)
{
return MultOperation<Terminal<T1>, Base<T2, R2>, typename boost::common_type<T1, R2>::type>(Terminal<T1>(u), v);
}
template <typename T1, typename T2, typename R1, typename R2>
MultOperation<Base<T1, R1>, Terminal<T2>, typename boost::common_type<R1, T2>::type>
operator*( Base<T1, R1> const& u, T2 const& v)
{
return MultOperation<Base<T1, R1>, Terminal<T2>, typename boost::common_type<R1, T2>::type>(u, Terminal<T2>, v);
}
现在,我无法使用任何新的 C++ 功能。 (这是删除旧库的一些重构的一部分,因此我们可以升级到新的 cpp 标准。)不过我可以使用 boost 东西。我在想我的答案可能在于 boost::enable_if
东西,但我所有的尝试都进入了死胡同。现在,请记住,目标是表达式模板,所以我不能为传入的数据做任何转换。是的......它太复杂了......我希望你有一些魔法。
问题的简短版本:
我怎样才能让 (1 * Derived) * Derived
匹配第一个运算符的 operator(T, Base)
,然后匹配第二个运算符的 operator(Base, Base)
?
它目前匹配第一个罚款,然后第二个匹配到一个 Base 泛型运算符,因为 T 不进行转换,因此比 Base 匹配得更好。
这是一个测试 class 是否是某种 Base
的特征:
template<class T>
struct is_some_kind_of_Base {
typedef char yes;
typedef struct { char _[2]; } no;
template<class U, class V>
static yes test(Base<U, V> *);
static no test(...);
static const bool value = (sizeof(test((T*)0)) == sizeof(yes));
};
然后约束你后面的两个 operator*
像:
template <typename T1, typename T2, typename R2>
typename boost::disable_if<is_some_kind_of_Base<T1>,
MultOperation<Terminal<T1>, Base<T2, R2>,
typename boost::common_type<T1, R2>::type> >::type
operator*( T1 const& u, Base<T2, R2> const& v) { /* ... */ }
Demo.
为防止common_type
导致硬错误,我们需要推迟其评估。
template <class T1, class T2, class R1, class R2>
struct make_mult_operation {
typedef MultOperation<T1, T2, typename boost::common_type<R1, R2>::type> type;
};
template <typename T1, typename T2, typename R2>
typename boost::disable_if<is_some_kind_of_Base<T1>,
make_mult_operation<Terminal<T1>, T2, T1, R2> >::type::type
operator*( T1 const& u, Base<T2, R2> const& v) { /* ... */ }
Demo.
我理解你的问题,你想为给定基类型派生的类型专门化一个 class 模板。我举一个没有那么多模板参数的例子。
正如您所建议的,这里的想法是通过 enable_if
select 重载(我使用了 std::enable_if
class 但您可以简单地替换 std::
by boost::
为您的目的):
template<typename T, typename U>
struct is_derived_from_base
{
static constexpr bool first = std::is_base_of<Base<T>, T>::value;
static constexpr bool second = std::is_base_of<Base<U>, U>::value;
static constexpr bool none = !first && !second;
static constexpr bool both = first && second;
static constexpr bool only_first = first && !second;
static constexpr bool only_second = !first && second;
};
template<typename T, typename U, typename = typename std::enable_if<is_derived_from_base<T,U>::none>::type >
auto operator*(T const& t, U const& u)
{
std::cout<<"Both T and U are not derived"<<std::endl;
return MultOperation<T, U>(t,u);
}
template<typename T, typename U, typename = typename std::enable_if<is_derived_from_base<T,U>::only_first>::type >
auto operator*(Base<T> const& t, U const& u)
{
std::cout<<"T is derived from Base<T>, U is not derived"<<std::endl;
return MultOperation<Base<T>, U>(t,u);
}
template<typename T, typename U, typename = typename std::enable_if<is_derived_from_base<T,U>::only_second>::type >
auto operator*(T const& t, Base<U> const& u)
{
std::cout<<"T is not derived, U is derived from Base<U>"<<std::endl;
return MultOperation<T, Base<U> >(t,u);
}
template<typename T, typename U, typename = typename std::enable_if<is_derived_from_base<T,U>::both>::type >
auto operator*(Base<T> const& t, Base<U> const& u)
{
std::cout<<"T is derived from Base<T>, U is derived from Base<U>"<<std::endl;
return MultOperation<Base<T>, Base<U> >(t,u);
}
有关详细信息,请参阅完整程序 here。
我有一组运算符,我需要为表达式模板覆盖这些运算符。我希望基类型的所有派生 类 都与基类型匹配。然后其他的东西会被通用类型捕获。不幸的是,泛型类型先于基类型获取派生类型。为了使事情变得美好和混乱,一切都被大量模板化,包括一些 CRTP。让我试着给出一个更简单的代码版本:
// Note: 'R' is used for return type
template <typename DerivedType, typename R>
class Base
{ // ...
};
template <typename E1, typename E2, typename R>
class MultOperation : public Base<MultOperation<E1, E2, R>, R>
{ // ...
};
template <typename T>
class Terminal : public Base<Terminal<T>, T>
{ // ...
};
// The broken operators:
template <typename T1, typename T2, typename R1, typename R2>
MultOperation<Base<T1, R1>, Base<T2, R2>, typename boost::common_type<R1, R2>::type>
operator*( Base<T1, R1> const& u, Base<T2, R2> const& v)
{
return MultOperation<Base<T1, R1>, Base<T2, R2>, typename boost::common_type<R1, R2>::type>(u, v);
}
template <typename T1, typename T2, typename R1, typename R2>
MultOperation<Terminal<T1>, Base<T2, R2>, typename boost::common_type<T1, R2>::type>
operator*( T1 const& u, Base<T2, R2> const& v)
{
return MultOperation<Terminal<T1>, Base<T2, R2>, typename boost::common_type<T1, R2>::type>(Terminal<T1>(u), v);
}
template <typename T1, typename T2, typename R1, typename R2>
MultOperation<Base<T1, R1>, Terminal<T2>, typename boost::common_type<R1, T2>::type>
operator*( Base<T1, R1> const& u, T2 const& v)
{
return MultOperation<Base<T1, R1>, Terminal<T2>, typename boost::common_type<R1, T2>::type>(u, Terminal<T2>, v);
}
现在,我无法使用任何新的 C++ 功能。 (这是删除旧库的一些重构的一部分,因此我们可以升级到新的 cpp 标准。)不过我可以使用 boost 东西。我在想我的答案可能在于 boost::enable_if
东西,但我所有的尝试都进入了死胡同。现在,请记住,目标是表达式模板,所以我不能为传入的数据做任何转换。是的......它太复杂了......我希望你有一些魔法。
问题的简短版本:
我怎样才能让 (1 * Derived) * Derived
匹配第一个运算符的 operator(T, Base)
,然后匹配第二个运算符的 operator(Base, Base)
?
它目前匹配第一个罚款,然后第二个匹配到一个 Base 泛型运算符,因为 T 不进行转换,因此比 Base 匹配得更好。
这是一个测试 class 是否是某种 Base
的特征:
template<class T>
struct is_some_kind_of_Base {
typedef char yes;
typedef struct { char _[2]; } no;
template<class U, class V>
static yes test(Base<U, V> *);
static no test(...);
static const bool value = (sizeof(test((T*)0)) == sizeof(yes));
};
然后约束你后面的两个 operator*
像:
template <typename T1, typename T2, typename R2>
typename boost::disable_if<is_some_kind_of_Base<T1>,
MultOperation<Terminal<T1>, Base<T2, R2>,
typename boost::common_type<T1, R2>::type> >::type
operator*( T1 const& u, Base<T2, R2> const& v) { /* ... */ }
Demo.
为防止common_type
导致硬错误,我们需要推迟其评估。
template <class T1, class T2, class R1, class R2>
struct make_mult_operation {
typedef MultOperation<T1, T2, typename boost::common_type<R1, R2>::type> type;
};
template <typename T1, typename T2, typename R2>
typename boost::disable_if<is_some_kind_of_Base<T1>,
make_mult_operation<Terminal<T1>, T2, T1, R2> >::type::type
operator*( T1 const& u, Base<T2, R2> const& v) { /* ... */ }
Demo.
我理解你的问题,你想为给定基类型派生的类型专门化一个 class 模板。我举一个没有那么多模板参数的例子。
正如您所建议的,这里的想法是通过 enable_if
select 重载(我使用了 std::enable_if
class 但您可以简单地替换 std::
by boost::
为您的目的):
template<typename T, typename U>
struct is_derived_from_base
{
static constexpr bool first = std::is_base_of<Base<T>, T>::value;
static constexpr bool second = std::is_base_of<Base<U>, U>::value;
static constexpr bool none = !first && !second;
static constexpr bool both = first && second;
static constexpr bool only_first = first && !second;
static constexpr bool only_second = !first && second;
};
template<typename T, typename U, typename = typename std::enable_if<is_derived_from_base<T,U>::none>::type >
auto operator*(T const& t, U const& u)
{
std::cout<<"Both T and U are not derived"<<std::endl;
return MultOperation<T, U>(t,u);
}
template<typename T, typename U, typename = typename std::enable_if<is_derived_from_base<T,U>::only_first>::type >
auto operator*(Base<T> const& t, U const& u)
{
std::cout<<"T is derived from Base<T>, U is not derived"<<std::endl;
return MultOperation<Base<T>, U>(t,u);
}
template<typename T, typename U, typename = typename std::enable_if<is_derived_from_base<T,U>::only_second>::type >
auto operator*(T const& t, Base<U> const& u)
{
std::cout<<"T is not derived, U is derived from Base<U>"<<std::endl;
return MultOperation<T, Base<U> >(t,u);
}
template<typename T, typename U, typename = typename std::enable_if<is_derived_from_base<T,U>::both>::type >
auto operator*(Base<T> const& t, Base<U> const& u)
{
std::cout<<"T is derived from Base<T>, U is derived from Base<U>"<<std::endl;
return MultOperation<Base<T>, Base<U> >(t,u);
}
有关详细信息,请参阅完整程序 here。