【问题标题】:Class function template definition outside of the class类外的类函数模板定义
【发布时间】:2019-11-16 17:39:11
【问题描述】:

所以我一直在编写自己的 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 时出现错误:“无法在类模板成员的声明中指定默认模板参数在其类之外”尽管代码仍在编译

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): could not deduc template argument for «unnamed-symbol»”

但如果我在类中编写实现,错误就会消失。我该如何解决这个问题?

【问题讨论】:

  • 尝试将template&lt;bool, typename&gt; 替换为template&lt;bool _arr, typename&gt;。看起来像是 Microsoft 编译器中的错误。
  • 哇,这对所有事情都有很大帮助,我还应该注意到我正在使用 Visual Studio 19。这个错误是否总是在这种情况下发生?我应该注意什么?
  • 这些错误非常难以预测,并且没有通用的方法来避免它们以及如何修复它们。谈到模板,我在 MSVC 中看到过很多模板。我建议尝试另一个编译器(例如 gcc - 请参阅example),如果它编译代码,请修改它以使其与 MSVC 一起使用。添加冗余参数名称纯粹是我的猜测。

标签: c++ templates sfinae


【解决方案1】:

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

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

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

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

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

编辑

考虑以下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 拒绝此定义并出现错误:“无法推断«未命名符号»的模板参数”。可以通过为bool 参数添加名称来修复它:

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

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

【讨论】:

  • 那么我什么时候应该使用假模板呢?使用 std::enable_if 还是 type_traits 中需要它的任何其他功能?该模板参数是否与类模板参数同时被评估?
  • @uneven_mark,我更新了我的答案并确认 OP 的 SFINAE 是错误的。
  • 是的,我也意识到了。
  • @Denomycor,当你需要 SFINAE 时,你会使用假模板。 SFINAE 仅在模板被实例化时起作用,仅当条件取决于模板参数并且仅当错误发生在此处(立即上下文)时才起作用。 enable_if 是实现 SFINAE 的规范模板,但没有它也可以实现。
  • @Denomycor, BTW, has_reset 是使用 SFINAE 实现的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-07-22
  • 2016-10-19
相关资源
最近更新 更多