我花了大约一个小时来重新实现C++头<array>。大多数东西都在名称空间my_std中,但是tuple_size等等不是,否则它们就没用了。我的目标是C++14。
请注意:array<T, N>::swap成员函数的function规范太复杂了,我选择不重新实现std::is_nothrow_swappable特性,这在C++17之前是不可用的。
我使用优先选择作为参考。不过,我并没有检查所有的东西,而且可能有不符合标准的东西或从C++17取走的东西。
这是我的代码,在300行内:(不包括空行和注释)
// array.hpp
// C++14 std::array implementation
#ifndef INC_ARRAY_HPP_JCr9Lp1ED0
#define INC_ARRAY_HPP_JCr9Lp1ED0
#include <algorithm>
#include <cstddef>
#include <initializer_list>
#include <iterator>
#include <stdexcept>
#include <tuple>
#include <type_traits>
#include <utility>
namespace my_std {
    template <class T, std::size_t N>
    struct array {
    private:
        void error() const
        {
            throw std::out_of_range{ "array out of range" };
        }
    public:
        using value_type = T;
        using size_type = std::size_t;
        using difference_type = std::ptrdiff_t;
        using reference = T&;
        using const_reference = const T&;
        using pointer = T*;
        using const_pointer = const T*;
        using iterator = T*;
        using const_iterator = const T*;
        using reverse_iterator = std::reverse_iterator<iterator>;
        using const_reverse_iterator = std::reverse_iterator<const_iterator>;
        constexpr bool empty() const noexcept
        {
            return N == 0;
        }
        constexpr size_type size() const noexcept
        {
            return N;
        }
        constexpr size_type max_size() const noexcept
        {
            return N;
        }
        T& at(std::size_t pos)
        {
            if (pos >= N)
                this->error();
            return elems[pos];
        }
        constexpr const T& at(std::size_t pos) const
        {
            if (pos >= N)
                this->error();
            return elems[pos];
        }
        T& operator[](std::size_t pos)
        {
            return elems[pos];
        }
        constexpr const T& operator[](std::size_t pos) const
        {
            return elems[pos];
        }
        T& front()
        {
            return elems[0];
        }
        constexpr const T& front() const
        {
            return elems[0];
        }
        T& back()
        {
            return elems[N - 1];
        }
        constexpr const T& back() const
        {
            return elems[N - 1];
        }
        T* data() noexcept
        {
            return elems;
        }
        constexpr const T* data() const noexcept
        {
            return elems;
        }
        T* begin() noexcept
        {
            return elems;
        }
        const T* begin() const noexcept
        {
            return elems;
        }
        const T* cbegin() const noexcept
        {
            return elems;
        }
        T* end() noexcept
        {
            return begin() + N;
        }
        const T* end() const noexcept
        {
            return begin() + N;
        }
        const T* cend() const noexcept
        {
            return begin() + N;
        }
        auto rbegin() noexcept
        {
            return std::make_reverse_iterator(end());
        }
        auto rbegin() const noexcept
        {
            return std::make_reverse_iterator(end());
        }
        auto crbegin() const noexcept
        {
            return std::make_reverse_iterator(end());
        }
        auto rend() noexcept
        {
            return std::make_reverse_iterator(begin());
        }
        auto rend() const noexcept
        {
            return std::make_reverse_iterator(begin());
        }
        auto crend() const noexcept
        {
            return std::make_reverse_iterator(begin());
        }
        void fill(const T& value)
        {
            std::fill_n(elems, N, value);
        }
        /*
        Note: is_nothrow_swappable_v
        is not available prior to C++17.
        I am not going to the trouble to
        implement it from scratch.
        So the noexcept specification of swap
        is left unimplemented.
        */
        void swap(array& other) /*noexcept(std::is_swappable_v<T>)*/
        {
            std::swap_ranges(begin(), end(), other.begin());
        }
        T elems[N];
    };
    template <class T>
    struct array<T, 0> {
    private:
        void error() const
        {
            throw std::out_of_range{ "array out of range" };
        }
    public:
        using value_type = T;
        using size_type = std::size_t;
        using difference_type = std::ptrdiff_t;
        using reference = T&;
        using const_reference = const T&;
        using pointer = T*;
        using const_pointer = const T*;
        using iterator = T*;
        using const_iterator = const T*;
        using reverse_iterator = std::reverse_iterator<iterator>;
        using const_reverse_iterator = std::reverse_iterator<const_iterator>;
        constexpr bool           empty() const noexcept { return true; }
        constexpr std::size_t     size() const noexcept { return 0; }
        constexpr std::size_t max_size() const noexcept { return 0; }
                        T&         at(std::size_t pos) { this->error(); }
        constexpr const T&         at(std::size_t pos) const { this->error(); }
                        T& operator[](std::size_t pos) { this->error(); }
        constexpr const T& operator[](std::size_t pos) const { this->error(); }
                        T& front()                { this->error(); }
        constexpr const T& front() const          { this->error(); }
                        T&  back()                { this->error(); }
        constexpr const T&  back() const          { this->error(); }
                        T*  data()       noexcept { return nullptr; }
        constexpr const T*  data() const noexcept { return nullptr; }
              T*   begin()       noexcept { return nullptr; }
        const T*   begin() const noexcept { return nullptr; }
        const T*  cbegin() const noexcept { return nullptr; }
              T*     end()       noexcept { return nullptr; }
        const T*     end() const noexcept { return nullptr; }
        const T*    cend() const noexcept { return nullptr; }
              T*  rbegin()       noexcept { return nullptr; }
        const T*  rbegin() const noexcept { return nullptr; }
        const T* crbegin() const noexcept { return nullptr; }
              T*    rend()       noexcept { return nullptr; }
        const T*    rend() const noexcept { return nullptr; }
        const T*   crend() const noexcept { return nullptr; }
        void fill(const T& value) {}
        void swap(array& other) noexcept {}
    };
    template <class T, std::size_t N>
    inline bool operator==(const array<T, N>& lhs,
                           const array<T, N>& rhs)
    {
        return std::equal(lhs.begin(), lhs.end(),
                          rhs.begin(), rhs.end());
    }
    template <class T, std::size_t N>
    inline bool operator!=(const array<T, N>& lhs,
                           const array<T, N>& rhs)
    {
        return !(lhs == rhs);
    }
    template <class T, std::size_t N>
    inline bool operator< (const array<T, N>& lhs,
                           const array<T, N>& rhs)
    {
        return std::lexicographical_compare(lhs.begin(), lhs.end(),
                                            rhs.begin(), rhs.end());
    }
    template <class T, std::size_t N>
    inline bool operator<=(const array<T, N>& lhs,
                           const array<T, N>& rhs)
    {
        return !(rhs < lhs);
    }
    template <class T, std::size_t N>
    inline bool operator> (const array<T, N>& lhs,
                           const array<T, N>& rhs)
    {
        return rhs < lhs;
    }
    template <class T, std::size_t N>
    inline bool operator>=(const array<T, N>& lhs,
                           const array<T, N>& rhs)
    {
        return !(lhs < rhs);
    }
    template <std::size_t I, class T, std::size_t N>
    constexpr T& get(array<T, N>& a) noexcept
    {
        return a[I];
    }
    template <std::size_t I, class T, std::size_t N>
    constexpr T&& get(array<T, N>&& a) noexcept
    {
        return static_cast<T&&>(a[I]);
    }
    template <std::size_t I, class T, std::size_t N>
    constexpr const T& get(const array<T, N>& a) noexcept
    {
        return a[I];
    }
    template <class T, std::size_t N>
    void swap(array<T, N>& lhs, array<T, N>& rhs)
        noexcept(noexcept(lhs.swap(rhs)))
    {
        return lhs.swap(rhs);
    }
}
namespace std {
    template <class T, std::size_t N>
    class tuple_size<my_std::array<T, N>>
        :public std::integral_constant<std::size_t, N> {
    };
    template <std::size_t I, class T, std::size_t N>
    struct tuple_element<I, my_std::array<T, N>> {
        using type = T;
    };
}
#endif包含保护的命名只包含一个随机字符串,以避免名称冲突。一般来说,我不喜欢(C++)宏。
发布于 2019-02-14 21:10:31
如果定义了所有类型,为什么不使用它们呢?
    const T* begin() const noexcept
    // More informative to write:
    const_iterator begin() const noexcept发布于 2019-02-15 03:11:00
请注意:
array<T, N>::swap成员函数的noexcept规范太复杂了,我选择不重新实现C++17之前不可用的std::is_nothrow_swappable特性。
如果一个类型不存在,写你自己的。C++11/14忽略了许多在17中添加时不需要语言支持的库特性。is_nothrow_swappable是这些库特性之一(也是3种简单的测试结构)。
我用reference作为参考。不过,我并没有检查所有的东西,而且可能有不符合标准的东西或从C++17取走的东西。
您应该使用C++14标准或接近最终C++14标准的草稿版本。N4140是C++14发布后的第一稿。
        void error() const
        {
            throw std::out_of_range{ "array out of range" };
        }您的函数在这里可以更好地命名。我甚至会考虑将其概括为接受任何const char*消息,并使其成为一个免费的函数。
        constexpr bool empty() const noexcept
        {
            return N == 0;
        }由于您专门处理N是0的情况,此函数将始终返回false。
发布于 2019-02-14 23:51:23
另一点:而不是
if (pos >= N)
    this->error();这就足够了
if (pos >= N)
    error();因为this->是多余的。
https://codereview.stackexchange.com/questions/213443
复制相似问题