我花了大约一个小时来重新实现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
复制相似问题