如何对继承构造函数执行正确的SFINAE

How do I do proper SFINAE on inheriting constructors?

本文关键字:SFINAE 执行 构造函数 继承      更新时间:2023-10-16

我正在尝试对构造函数执行SFINAE。我想为整数启用一个重载,为其他所有重载启用一个。我知道我可以只做一个base(int)base(T)构造函数,但我想用这种方式来做。

template <class T>
struct base
{
    template <class T1 = T>
    base(T1, typename std::enable_if<std::is_same<T1, int>::value>::type* = nullptr) {}
    template <class T1 = T>
    base(T1, typename std::enable_if<!std::is_same<T1, int>::value>::type* = nullptr) {}
};

然后,我创建了一个继承构造函数的主Base类:

template <class T>
struct Base : base<T>
{
    using base<T>::base;
};

但当我用任何T实例化Base时,我会得到以下错误:

source_file.cpp:21:15: error: call to deleted constructor of 'Base<int>'
    Base<int> v(4);
              ^ ~
source_file.cpp:16:25: note: deleted constructor was inherited here
    using base<T>::base;
                        ^
source_file.cpp:7:5: note: constructor cannot be inherited
    base(T1, typename std::enable_if<std::is_same<T1, int>::value>::type* = nullptr) {}
    ^

当我直接实例化base时,它可以毫无问题地工作。为什么我在做SFINAE时不能继承构造函数?如果没有第二个构造函数重载,一切都可以正常工作。

您可以在示例程序中完全避免这个问题,方法是在base中只定义泛型构造函数,并为base<int>(DEMO)提供专门化:

template <class T>
struct base
{
    base(T) { std::cout << "generic constructorn"; }
};
template <>
base<int>::base(int) { std::cout << "int specializationn"; }

或者使用标签调度而不是SFINAE(DEMO):

template <class T>
class base
{
    base(std::true_type, int) { std::cout << "int specializationn"; }
    base(std::false_type, T) { std::cout << "generic constructorn"; }
public:
    base(T t) : base(std::is_same<int, T>{}, std::move(t)) {}
};

与构造函数的参数列表不同,整个模板参数列表总是继承的,包括默认的模板参数。把SFINAE放在那里:

template <class T>
struct base
{
    template <class T1 = T,
      typename std::enable_if<std::is_same<T1, int>::value>::type* = nullptr>
    base(T1) {}
    template <class T1 = T,
      typename std::enable_if<!std::is_same<T1, int>::value>::type* = nullptr>
    base(T1) {}
};

这在Clang和GCC中工作,在-std=c++11-std=c++1z模式中工作。(仅测试了最新版本,分别为3.6和5.1。)