在模板参数中通过SFINAE选择构造函数

Select constructor through SFINAE in template arguments

本文关键字:SFINAE 选择 构造函数 参数      更新时间:2023-10-16

我试图通过SFINAE选择一个构造函数,如下所示:

template<typename T>
class MyClass
{
public:
    template<typename C, typename = std::enable_if_t<std::is_class<C>::value>>
    MyClass(C) { }
    template<typename C, typename = std::enable_if_t<std::is_pointer<C>::value>>
    MyClass(C) { }
};

但是编译器报错如下:

错误C2535: 'MyClass::MyClass(C)':成员函数已定义或声明

,甚至不实例化构造函数。

我制定了一个工作但丑陋的解决方案,我不想使用,因为额外的未使用的参数:

template<typename T>
class MyWorkingClass
{
public:
    template<typename C>
    MyWorkingClass(C, std::enable_if_t<std::is_class<C>::value>* = nullptr) { }
    template<typename C>
    MyWorkingClass(C, std::enable_if_t<std::is_pointer<C>::value>* = nullptr) { }
};

这里有一个简短的用法示例:

void* ptr = nullptr;
MyClass<int> mc1(ptr);
std::vector<int> vec;
MyClass<int> mc2(vec);
// Shall raise an error
// MyClass<int> mc2(0);

性状std::is_pointerstd::is_class只是一个例子,原始性状更为复杂。

是否有一种方法可以通过SFINAE选择构造函数,而无需向构造函数添加另一个参数(可能非常接近第一种方法)?

问题是参数的默认值不是模板方法签名的一部分。所以你有两个相同的template<class C,class>ctor(c)因子

template<class T>
struct MyClass {
  template<class C,
    std::enable_if_t<std::is_class<C>{}>* =nullptr
  >
  MyClass(C) { }
  template<class C,
    std::enable_if_t<std::is_pointer<C>{}>* =nullptr
  >
  MyClass(C) { }
};

这里使用依赖类型的模板值实参。它们从不冲突,因为指针模板实参的类型取决于类型实参。

再添加一个(虚拟)类型模板参数:

template <typename C
        , typename = std::enable_if_t<std::is_class<C>::value>>
MyClass(C) { }
template <typename C
        , typename = std::enable_if_t<std::is_pointer<C>::value>
        , typename = void>
//        ~~~~~~~~~~~~~~^
MyClass(C) { }

这样可能更简单:

template<typename T>
class MyClass {
public:
    template<typename C, typename = std::enable_if_t<std::is_class<C>::value>>
    MyClass(C) { }
    template<typename C>
    MyClass(C*) { }
};

并非所有这些都需要由std::enable_if_t完成。