类外的类功能模板定义

时间:2019-11-16 17:39:11

标签: c++ templates sfinae

因此,我一直在编码自己的unique_ptr类,并且必须以与处理其他类型不同的方式来处理数组。

template <typename TYPE, bool _arr = std::is_array<TYPE>::value>
    class scoped_ptr final {
    private:
        typedef std::remove_extent_t<TYPE> gen;
        gen* m_data;
    public:
        //some methods
        void reset();
    };

template<typename TYPE ,bool _arr>
    inline void scoped_ptr<TYPE, _arr>::reset()
    {
    //some code...
    }

问题是当我使用std :: enable_if时,我希望reset方法仅可用于非数组分配,如果出现错误:“无法在类模板的成员的声明上指定默认模板参数仍在类之外”尽管代码仍在编译

template<typename TYPE ,bool _arr>
    class scoped_ptr final {
    public:
        template<typename = typename std::enable_if<!_arr>::type>
        void reset();
    };

template<typename TYPE ,bool _arr>
template<typename = typename std::enable_if<!_arr>::type>
    inline void scoped_ptr<TYPE, _arr>::reset()
    {
    }

我也尝试过这种方法,但是它也给出了一个错误:“模板参数列表必须与参数列表匹配”

template<typename TYPE ,bool _arr>
    inline void scoped_ptr<TYPE, false>::reset()
    {
    }

有人对如何禁用该数组方法有想法吗?我知道我可以专门研究scoped_ptr类,但我想避免代码重复。有什么办法吗?

编辑:

阅读回复后,我将代码更改为此:

template <typename TYPE, bool is_arr = std::is_array<TYPE>::value>
    class scoped_ptr final {
    private:
        typedef std::remove_extent_t<TYPE> gen;
        gen* m_data;

    public:
        //Some methods

        template<bool _arr = is_arr, typename = typename std::enable_if<!_arr>::type>
        void reset();

    };


    template<typename TYPE, bool is_arr>
    template<bool, typename>
    inline void scoped_ptr<TYPE, is_arr>::reset()
    {
    }

代码编译没有错误,直到我尝试调用该方法为止:

int main() {

    scoped_ptr<int> ptr = new int;
    ptr.reset();
}

那是当我收到错误消息:“ void scoped_ptr«int,false»:: reset(void):无法推断出«unnamed-symbol»的模板参数”

但是如果我在类内部编写实现,错误就会消失。我该如何解决?

1 个答案:

答案 0 :(得分:3)

如果您想使reset()对SFINAE友好,请将其设为假模板:

template<bool is_arr = _arr, typename = std::enable_if_t<is_arr>>
void reset();

请注意,当实例化模板时SFINAE起作用,并且条件应取决于template参数。这不是有效的SFINAE构造:

template<typename = typename std::enable_if<!_arr>::type>
void reset();

如果您不关心SFINAE的友好性,请在static_assert()中使用reset()

编辑

考虑以下simple code作为有效和无效SFINAE的演示:

template<class T, bool f = std::is_integral_v<T>>
struct S {
    // template<typename = std::enable_if_t<f>>                 // (invalid)
    template<bool _f = f, typename = std::enable_if_t<_f>>      // (valid)
    void reset() {}
};

template<class T, typename = void>
struct has_reset : std::false_type {};

template<class T>
struct has_reset<T, std::void_t<decltype(std::declval<T>().reset())>> : std::true_type {};

void foo() {
    has_reset<S<int>>::value;
    has_reset<S<void>>::value;
}

如果用(valid)替换(invalid)行,它将无法编译。

编辑2。

在类外定义成员函数时,您无需重复模板参数的默认值:

template<class T, bool f>
template<bool, typename>
void S<T, f>::reset() { }

编辑3。

由于某种原因(我想是编译器错误),MSVC拒绝此定义,并出现以下错误:“无法推断出«unnamed-symbol»的模板参数”。可以通过为bool参数添加名称来解决该问题:

template<class T, bool f>
template<bool _f, typename>
void S<T, f>::reset() { }

此名称应与声明中的名称匹配。