C++:将模板的类型参数部分特化为另一个模板 class 的成员类型
C++: Partially specializing template's type parameter as another template class's member-type
我有一个包含成员结构 SZug
:
的模板结构 SFoo
template <typename tTYPE>
struct SFoo
{
struct SZug {};
};
我有另一个采用类型参数的结构 SBar
:
template <typename tTYPE>
struct SBar
{ /* stuff */ };
我想使用 SZug
作为类型参数来专门化 SBar
,如下所示:
template <typename tTYPE>
struct SBar<typename SFoo<tTYPE>::SZug>
{ /* different stuff */ };
这无法编译 - LLVM 输出:
non-deducible template parameter 'tTYPE'
虽然编译器可以根据需要轻松推断出这一点,但我猜只是 C++ 规范需要专门涵盖这种情况。
有什么办法可以实现吗?
(注意:我目前正在通过将 SZug
移到 SFoo
之外并使用 using
声明来解决它,但它很难看。)
您可以通过以下内容(打印出 0 1,BTW)获得您正在寻找的 效果:
#include <type_traits>
#include <iostream>
namespace detail
{
struct SZugBase{};
}
template <typename tTYPE>
struct SFoo
{
struct SZug : public detail::SZugBase {};
};
template<typename tType, bool IsFoo>
struct SBarBase
{
int value = 0;
};
template<typename tType>
struct SBarBase<tType, true>
{
int value = 1;
};
template <typename tTYPE>
struct SBar : public SBarBase<tTYPE, std::is_convertible<tTYPE, detail::SZugBase>::value>
{ /* stuff */ };
int main()
{
SBar<int> b0;
SBar<SFoo<int>::SZug> b1;
std::cout << b0.value << " " << b1.value << std::endl;
}
说明
首先,我们给SZug
一个常规-class基数:
namespace detail
{
struct SZugBase{};
}
template <typename tTYPE>
struct SFoo
{
struct SZug : public detail::SZugBase {};
};
注意以下几点:
SZugBase
没有任何参数化,所以很容易独立于SFoo
的参数来引用它
SZugBase
位于 detail
命名空间中,因此,根据常见的 C++ 约定,您告诉代码的客户忽略它。
现在我们给 SBar
两个基数 classes,专门研究某些东西是否可以转换为 SZug
的非模板基数:
template<typename tType, bool IsFoo>
struct SBarBase
{
int value = 0;
};
template<typename tType>
struct SBarBase<tType, true>
{
int value = 1;
};
最后,我们只需要将 SBar
作为这些基础的子class(取决于专业化):
template <typename tTYPE>
struct SBar : public SBarBase<tTYPE, std::is_convertible<tTYPE, detail::SZugBase>::value>
{ /* stuff */ };
请注意,您在这里没有特化 SBar
,而是特化基础 class。不过,这有效地产生了相同的效果。
我不确定我是否完全理解你想要做什么,但你可以尝试以下操作(它只需要向 SZug
添加特定属性:
template <typename tTYPE>
struct SFoo {
struct SZug {
// Add this to be able to obtain SFoo<T> from SFoo<T>::SZug
using type = tTYPE;
};
};
然后是一个小模板来检查类型是否是 SFoo<T>::SZug
:
template <typename tTYPE, typename Enabler = void>
struct is_SZug: public std::false_type { };
template <typename tTYPE>
struct is_SZug<tTYPE, typename std::enable_if<
std::is_same<tTYPE, typename SFoo<typename tTYPE::type>::SZug>{}
>::type>: public std::true_type { };
并对 SBar
模板稍作修改以启用 "specialization" 如果类型是 SZug
:
template <typename tTYPE, typename Enabler = void>
struct SBar
{ static void g(); };
template <typename tTYPE>
struct SBar<tTYPE, typename std::enable_if<is_SZug<tTYPE>{}>::type>
{ static void f(); };
一点检查:
void f () {
SBar<int>::g();
SBar<SFoo<int>::SZug>::f();
}
注意:也可以直接将SFoo<T>
设置为SFoo<T>::SZug
中的type
属性,只需要修改第二个即可std::is_same
的说法有点。
我有一个包含成员结构 SZug
:
SFoo
template <typename tTYPE>
struct SFoo
{
struct SZug {};
};
我有另一个采用类型参数的结构 SBar
:
template <typename tTYPE>
struct SBar
{ /* stuff */ };
我想使用 SZug
作为类型参数来专门化 SBar
,如下所示:
template <typename tTYPE>
struct SBar<typename SFoo<tTYPE>::SZug>
{ /* different stuff */ };
这无法编译 - LLVM 输出:
non-deducible template parameter 'tTYPE'
虽然编译器可以根据需要轻松推断出这一点,但我猜只是 C++ 规范需要专门涵盖这种情况。
有什么办法可以实现吗?
(注意:我目前正在通过将 SZug
移到 SFoo
之外并使用 using
声明来解决它,但它很难看。)
您可以通过以下内容(打印出 0 1,BTW)获得您正在寻找的 效果:
#include <type_traits>
#include <iostream>
namespace detail
{
struct SZugBase{};
}
template <typename tTYPE>
struct SFoo
{
struct SZug : public detail::SZugBase {};
};
template<typename tType, bool IsFoo>
struct SBarBase
{
int value = 0;
};
template<typename tType>
struct SBarBase<tType, true>
{
int value = 1;
};
template <typename tTYPE>
struct SBar : public SBarBase<tTYPE, std::is_convertible<tTYPE, detail::SZugBase>::value>
{ /* stuff */ };
int main()
{
SBar<int> b0;
SBar<SFoo<int>::SZug> b1;
std::cout << b0.value << " " << b1.value << std::endl;
}
说明
首先,我们给SZug
一个常规-class基数:
namespace detail
{
struct SZugBase{};
}
template <typename tTYPE>
struct SFoo
{
struct SZug : public detail::SZugBase {};
};
注意以下几点:
SZugBase
没有任何参数化,所以很容易独立于SFoo
的参数来引用它
SZugBase
位于detail
命名空间中,因此,根据常见的 C++ 约定,您告诉代码的客户忽略它。
现在我们给 SBar
两个基数 classes,专门研究某些东西是否可以转换为 SZug
的非模板基数:
template<typename tType, bool IsFoo>
struct SBarBase
{
int value = 0;
};
template<typename tType>
struct SBarBase<tType, true>
{
int value = 1;
};
最后,我们只需要将 SBar
作为这些基础的子class(取决于专业化):
template <typename tTYPE>
struct SBar : public SBarBase<tTYPE, std::is_convertible<tTYPE, detail::SZugBase>::value>
{ /* stuff */ };
请注意,您在这里没有特化 SBar
,而是特化基础 class。不过,这有效地产生了相同的效果。
我不确定我是否完全理解你想要做什么,但你可以尝试以下操作(它只需要向 SZug
添加特定属性:
template <typename tTYPE>
struct SFoo {
struct SZug {
// Add this to be able to obtain SFoo<T> from SFoo<T>::SZug
using type = tTYPE;
};
};
然后是一个小模板来检查类型是否是 SFoo<T>::SZug
:
template <typename tTYPE, typename Enabler = void>
struct is_SZug: public std::false_type { };
template <typename tTYPE>
struct is_SZug<tTYPE, typename std::enable_if<
std::is_same<tTYPE, typename SFoo<typename tTYPE::type>::SZug>{}
>::type>: public std::true_type { };
并对 SBar
模板稍作修改以启用 "specialization" 如果类型是 SZug
:
template <typename tTYPE, typename Enabler = void>
struct SBar
{ static void g(); };
template <typename tTYPE>
struct SBar<tTYPE, typename std::enable_if<is_SZug<tTYPE>{}>::type>
{ static void f(); };
一点检查:
void f () {
SBar<int>::g();
SBar<SFoo<int>::SZug>::f();
}
注意:也可以直接将SFoo<T>
设置为SFoo<T>::SZug
中的type
属性,只需要修改第二个即可std::is_same
的说法有点。