用于将模板专业化委托给默认实现的惯用 C++11

dol*_*hin 0 c++ templates c++11

我正在制作一个Box<T>处理一些数据的结构。具体细节并不重要。
然而,一个重要的注意事项是Box<T>可以存储指针,但也可能不能。所以 和Box<int>都是Box<int *>有效的。显然,如果我们拥有Box.data,我们将需要delete数据(如果它是指针类型)。这是我想出的适用于 C++11 的解决方案:

template <typename T> struct BoxTraits;

template <typename T> struct Box {
    using traits_t = BoxTraits<T>;

    T data;

    ~Box() = default; // not required, I know

    T get_data() { return traits_t::get_data(this); }
};

template <typename T> struct Box<T *> {
    using traits_t = BoxTraits<T *>;

    T *data;

    ~Box() { delete data; }

    T *get_data() { return traits_t::get_data(this); }
};

template <typename T> struct BoxTraits {
    static T get_data(Box<T> *const box) { return box->data; }
};
Run Code Online (Sandbox Code Playgroud)

Box::get_data这里是为了说明这种设计模式的一个问题。对于我想要添加的每一个方法Box,我需要在每个专业中添加一些样板。请注意,我还需要Box<T *const>专业化。

这似乎是一个相当垃圾的解决方案。在 C++14 中,我可以使用if constexpr特征is_ptr<T>,只需在需要专门化的方法中编写额外的代码...有什么方法可以在 C++11 中做到这一点吗?

这个解决方案更短、更干净并且适用于Box<U *const>!

template <typename T> struct is_ptr { static const bool value = false; };

template <typename U> struct is_ptr<U *> { static const bool value = true; };

template <typename U> struct is_ptr<U *const> {
    static const bool value = true;
};

template <typename T> struct Box {
    T data;

    ~Box() {
        if constexpr (is_ptr<T>::value) {
            delete data;
        }
    }

    T get_data() { return data; }
};
Run Code Online (Sandbox Code Playgroud)

Nel*_*eal 6

首先,C++11 已经有了std::is_pointer,不需要自己动手。您可以看到它继承自std::true_type或std::false_type代替定义其自己的value成员。原因是标签调度,if constexpr在这种情况下可以有效地替换:

template <typename T> struct Box {
    T data;

    ~Box() {
        destroy(std::is_pointer<T>{});
    }

private:
    void destroy(std::true_type) {
        delete data;
    }
    void destroy(std::false_type) {} // nothing to do
};
Run Code Online (Sandbox Code Playgroud)

演示

我认为这是 C++11 中根据类型特征委托给不同实现的最惯用的方式。在许多情况下,标签调度可以替代if constexpr(来自 C++17,而不是 C++14),并且我相信后者除了更清晰之外总是取代前者。如果您推出自己的类型特征,也可以在 C++11 之前使用标签调度。

最后注意:您不需要使用标准类型特征,您可以执行以下操作:

template <typename T> struct is_ptr { static const bool value = false; };
template <typename T> struct is_ptr<T*> { static const bool value = true; };
template <typename T> struct is_ptr<T* const> { static const bool value = true; };
template <typename T> struct is_ptr<T* volatile> { static const bool value = true; };
template <typename T> struct is_ptr<T* const volatile> { static const bool value = true; };

template<bool b>
struct bool_constant {};

template<typename T>
struct Box {
    T data;

    ~Box() {
        destroy(bool_constant<is_ptr<T>::value>{});
    }

private:
    void destroy(bool_constant<true>) {
        delete data;
    }
    void destroy(bool_constant<false>) {} // nothing to do
};
Run Code Online (Sandbox Code Playgroud)

演示

然而,这几乎相当于重新创建标准类型特征,但可能更糟。尽可能使用标准库。