SFINAE 将任何容器转换为 C 样式数组视图
SFINAE-ing any container into a c-style array view
我正在制作一个简单的非拥有数组视图 class:
template <typename T>
class array_view {
T* data_;
size_t len_;
// ...
};
我想从任何具有 data()
和 size()
成员函数的容器构造它,但是 SFINAE-d 正确地使得 array_view
只能从某些容器构造 C
如果实际遍历 data_
是有效且安全的行为。
我选择了:
template <typename C,
typename D = decltype(std::declval<C>().data()),
typename = std::enable_if_t<
std::is_convertible<D, T*>::value &&
std::is_same<std::remove_cv_t<T>,
std::remove_cv_t<std::remove_pointer_t<D>>>::value>
>
array_view(C&& container)
: data_(container.data()), len_(container.size())
{ }
这似乎完全不能令人满意,我什至不确定它是否正确。我是否正确地包含了所有正确的容器并排除了所有错误的容器?有没有更简单的方法来编写这个需求?
如果我们查看 N4512 中提议的 std::experimental::array_view
,我们会在 Table 104 中找到以下 Viewable
要求:
Expression Return type Operational semantics
v.size() Convertible to ptrdiff_t
v.data() Type T* such that T* is static_cast(v.data()) points to a
implicitly convertible to U*, contiguous sequence of at least
and is_same_v<remove_cv_t<T>, v.size() objects of (possibly
remove_cv_t<U>> is true. cv-qualified) type remove_cv_t<U>.
也就是说,作者对 .data()
使用基本相同的检查,但为 .size()
添加另一个检查。
为了通过对 T
的操作在 U
上使用指针算法,根据 [expr.add,类型需要 相似 ]]p6。 相似性 是为资格转换定义的,这就是为什么检查隐式可转换性然后检查相似性(通过 is_same
)足以进行指针运算的原因。
当然,不能保证操作语义。
在标准库中,唯一连续的容器是 std::array
和 std::vector
。还有 std::basic_string
有一个 .data()
成员,但是 std::initializer_list
没有,尽管它是连续的。
所有 .data()
成员函数都是为每个个体指定的 class,但它们都是 return 实际指针(无迭代器,无代理)。
这意味着检查 .data()
的存在对于标准库容器来说已经足够了;你想添加一个可转换性检查以使 array_view
不那么贪婪(例如 array_view<int>
拒绝一些 char* data()
)。
实现当然可以从接口中移开;你可以使用 Concepts,一个概念模拟,或者简单地使用 enable_if
和一个适当的类型函数。例如
template<typename T, typename As,
typename size_rt = decltype(std::declval<T>().size())
typename data_rt = decltype(std::declval<T>().data())>
constexpr bool is_viewable =
std::is_convertible_v<size_rt, std::ptrdiff_t>
&& std::is_convertible_v<data_rt, T*>
&& std::is_same_v<std::remove_cv_t<T>, std::remove_cv_t<data_rt>>;
template <typename C,
typename = std::enable_if_t<is_viewable<C, T>>
>
array_view(C&& container)
: data_(container.data()), len_(container.size())
{ }
是的,这并没有遵循类型函数的常用技术,但它更短,您明白了。
我正在制作一个简单的非拥有数组视图 class:
template <typename T>
class array_view {
T* data_;
size_t len_;
// ...
};
我想从任何具有 data()
和 size()
成员函数的容器构造它,但是 SFINAE-d 正确地使得 array_view
只能从某些容器构造 C
如果实际遍历 data_
是有效且安全的行为。
我选择了:
template <typename C,
typename D = decltype(std::declval<C>().data()),
typename = std::enable_if_t<
std::is_convertible<D, T*>::value &&
std::is_same<std::remove_cv_t<T>,
std::remove_cv_t<std::remove_pointer_t<D>>>::value>
>
array_view(C&& container)
: data_(container.data()), len_(container.size())
{ }
这似乎完全不能令人满意,我什至不确定它是否正确。我是否正确地包含了所有正确的容器并排除了所有错误的容器?有没有更简单的方法来编写这个需求?
如果我们查看 N4512 中提议的 std::experimental::array_view
,我们会在 Table 104 中找到以下 Viewable
要求:
Expression Return type Operational semantics v.size() Convertible to ptrdiff_t v.data() Type T* such that T* is static_cast(v.data()) points to a implicitly convertible to U*, contiguous sequence of at least and is_same_v<remove_cv_t<T>, v.size() objects of (possibly remove_cv_t<U>> is true. cv-qualified) type remove_cv_t<U>.
也就是说,作者对 .data()
使用基本相同的检查,但为 .size()
添加另一个检查。
为了通过对 T
的操作在 U
上使用指针算法,根据 [expr.add,类型需要 相似 ]]p6。 相似性 是为资格转换定义的,这就是为什么检查隐式可转换性然后检查相似性(通过 is_same
)足以进行指针运算的原因。
当然,不能保证操作语义。
在标准库中,唯一连续的容器是 std::array
和 std::vector
。还有 std::basic_string
有一个 .data()
成员,但是 std::initializer_list
没有,尽管它是连续的。
所有 .data()
成员函数都是为每个个体指定的 class,但它们都是 return 实际指针(无迭代器,无代理)。
这意味着检查 .data()
的存在对于标准库容器来说已经足够了;你想添加一个可转换性检查以使 array_view
不那么贪婪(例如 array_view<int>
拒绝一些 char* data()
)。
实现当然可以从接口中移开;你可以使用 Concepts,一个概念模拟,或者简单地使用 enable_if
和一个适当的类型函数。例如
template<typename T, typename As,
typename size_rt = decltype(std::declval<T>().size())
typename data_rt = decltype(std::declval<T>().data())>
constexpr bool is_viewable =
std::is_convertible_v<size_rt, std::ptrdiff_t>
&& std::is_convertible_v<data_rt, T*>
&& std::is_same_v<std::remove_cv_t<T>, std::remove_cv_t<data_rt>>;
template <typename C,
typename = std::enable_if_t<is_viewable<C, T>>
>
array_view(C&& container)
: data_(container.data()), len_(container.size())
{ }
是的,这并没有遵循类型函数的常用技术,但它更短,您明白了。