如果需要参数包,则使用SFINAE专门化类
Specializing class with SFINAE if a parameter pack is needed
因为我得到了这个问题的完美答案:SFINAE 专业课程
为了完整起见,我再次插入正确的解决方案作为示例:
class AA { public: using TRAIT = int; };
class BB { public: using TRAIT = float; };
template < typename T, typename UNUSED = void> class X;
template < typename T >
class X<T, typename std::enable_if< std::is_same< int, typename T::TRAIT>::value, void >::type>
{
public:
X() { std::cout << "First" << std::endl; }
};
template < typename T >
class X<T, typename std::enable_if< !std::is_same< int, typename T::TRAIT>::value, void >::type>
{
public:
X() { std::cout << "Second" << std::endl; }
};
int main()
{
X<AA> a;
X<BB> b;
}
但是,如果我必须使用一个参数包来进一步使用,我看不出有机会写下这样的东西:
template < typename T, typename ...S, typename UNUSED = void> class X;
错误:参数包"S"必须位于模板参数列表的末尾
以不同的顺序定义,如
template < typename T, typename UNUSED = void, typename ...S> class X;
如果使用第一个附加类型,则最终会出现问题。
好吧,我所描述的是一个我实际上找不到的技术解决方案。也许还有一个不同的。我的根本问题是:我需要两个不同的构造函数来调用不同的基类构造函数。但由于两个构造函数都有相同的参数集,我认为没有机会专门化构造函数本身。
如果specialized构造函数可以工作,它可以是这样的:
template < typename T>
class Y
{
public:
template <typename U = T, typename V= typename std::enable_if< std::is_same< int, typename U::TRAIT>::value, int >::type>
Y( const V* =nullptr) { std::cout << "First" << std::endl; }
template <typename U = T, typename V= typename std::enable_if< !std::is_same< int, typename U::TRAIT>::value, float >::type>
Y( const V* =nullptr) { std::cout << "Second" << std::endl; }
};
错误:"模板Y::Y(const V*)"不能重载
但如前所述。。。我不知道这能不能做到。
为了显示潜在的问题,我将给出以下示例,该示例显示了依赖于基类中定义的特性的基类构造函数的不同使用。
template <typename T, typename ... S>: public T
class Z
{
public:
// should work if T defines a trait
Z( typename T::SomeType t): T( t ) {}
// should be used if T defines another trait
Z( typename T::SomeType t): T( ) {}
};
而不是
template < typename T, typename ...S, typename UNUSED = void> class X;
你可以添加一层:
template <typename T, typename Dummy = void, typename ... Ts> class X_impl {};
然后
template <typename T, typename ...Ts>
using X = X_impl<T, void, Ts...>;
对于SFINAE,由于默认模板参数不是签名的一部分,
template <typename U = T,
typename V = std::enable_if_t<std::is_same<int, typename U::TRAIT>::value, int>>
Y(const V* = nullptr) { std::cout << "First" << std::endl; }
template <typename U = T,
typename V = std::enable_if_t<!std::is_same<int,
typename U::TRAIT>::value, float>>
Y(const V* = nullptr) { std::cout << "Second" << std::endl; }
应该重写,例如:
template <typename U = T,
std::enable_if_t<std::is_same<int, typename U::TRAIT>::value>* = nullptr>
Y() { std::cout << "First" << std::endl; }
template <typename U = T,
std::enable_if_t<!std::is_same<int, typename U::TRAIT>::value>* = nullptr>
Y() { std::cout << "Second" << std::endl; }
在C++20中,requires
可能会大大简化代码:
template <typename T>
class Y
{
public:
Y() requires(std::is_same< int, typename U::TRAIT>::value)
{ std::cout << "First" << std::endl; }
Y() requires(!std::is_same< int, typename U::TRAIT>::value)
{ std::cout << "Second" << std::endl; }
// ...
};
template<class...>struct types_tag{using type=types_tag;};
template<class...Ts>constexpr types_tag<Ts...> types{};
这些助手使您可以将许多类型作为一个bundle、一个参数进行传递。
现在你的类型X
可以看起来像:
template<class T, class types, class=void>
class X;
template<class T, class...Ts>>
class X<T, types_tag<Ts...>, std::enable_if_t<true>> {
};
X
的用户传入X<T, types_tag<int, double, char>
。
您可以编写如下适配器:
template<class T, class...Ts>
using X_t = X<T, types_tag<Ts...>>;
其中我们使using
别名具有比实现struct
更好的名称。
作为一个类型传递的类型束可以使一整束元编程变得简单。你可以传递多个包裹;并且您可以通过值将types_tag
传递给函数作为参数,以便于推断捆绑包的内容。
相关文章:
- 为什么使用SFINAE而不是函数重载
- 如何使用模板函数的函数签名进行SFINAE
- 数据成员SFINAE的C++17测试:gcc vs clang
- 使用在用于SFINAE的void_t中具有参数的方法
- 是否可以对零模板参数进行模板专门化
- 编译器如何在使用SFINAE的函数和标准函数之间确定两者是否可行
- 提供与TMP和SFINAE的通用接口
- "Inverse SFINAE"避免模棱两可的过载
- 尝试根据类中 typedef 的存在来专门化模板函数
- 表达式 SFINAE:如何根据类型是否包含具有一个或多个参数的函数来选择模板版本
- 如何基于模板化类的基类专门化成员函数
- 如何在儿童类中使用SFINAE
- 使用 SFINAE 作为模板参数的编译时递归
- 使用 SFINAE 设计模板方法
- 与SFINAE支票交朋友
- SFINAE的变分变量模板专门化
- 如果需要参数包,则使用SFINAE专门化类
- 为什么SFINAE在改变类模板专门化的位置时会弄乱?这是c++的bug吗?< / h1 >
- SFINAE使用模板、专门化和实现类型擦除
- 使用SFINAE部分专门化而不触及主模板