使用概念禁用 class 专业化
Disable class specialization using concepts
我正在使用 Concepts TS 实现我自己的 std::span
版本。我在执行 these constructors:
时卡住了
template<class Container> constexpr span(Container& cont);
template<class Container> constexpr span(const Container& cont);
备注:这些构造函数不应参与重载决策,除非:
Container
不是 span
和 的特化
Container
不是 array
的特化
如何使用概念来实现?
您可以使用类型特征来检查某个类型是否是 span
或 std::array
的特化。这对我有用:
#include <type_traits>
template<typename, std::ptrdiff_t> class span;
template <typename T>
struct is_array : std::false_type { };
template <typename T, size_t N>
struct is_array<std::array<T, N>> : std::true_type { };
template <typename T>
struct is_span : std::false_type { };
template <typename T, std::ptrdiff_t P>
struct is_span<span<T, P>> : std::true_type { };
template <typename T>
concept bool NotSpanNotArray = !is_array<T>::value && !is_span<T>::value;
template<typename, std::ptrdiff_t> class span {
public:
template<NotSpanNotArray T> constexpr span(T& cont);
// template<NotSpanNotArray T> constexpr span(const T& cont);
};
工作演示:https://wandbox.org/permlink/M0n60U8Hl4mpacuI
只是我不是100%确定这样的方案是否满足参与重载决议当且仅当的要求。一些语言律师可能会澄清这一点。
更新
我刚刚意识到 std::is_array
仅适用于 "ordinary" 数组,不适用于 std::array
。因此我也添加了一个自定义 is_array
类型特征。
首先,创建特征以检查专业化。 array
和 span
在接受类型参数和非类型参数的意义上看起来相同:
template <typename T, template <typename, auto> class Z>
struct is_specialization : std::false_type { };
template <typename A, auto V, template <typename, auto> class Z>
struct is_specialization<Z<A,V>, Z> : std::true_type { };
template <typename T, template <typename, auto> class Z>
inline constexpr bool is_specialization_v = is_specialization<T, Z>::value;
然后我们可以从中建立一个概念:
// the last bullet point
template <typename T, typename E>
concept ValidForElement =
ConvertibleTo<std::remove_pointer_t<T>(*)[], E(*)[]>;
template <typename T, typename E>
concept AllowedContainer =
// not a specialization of span (note: requires forward declaration of span)
!is_specialization_v<std::remove_cv_t<T>, std::span>
// not a specialization of array
&& !is_specialization_v<std::remove_cv_t<T>, std::array>
// not a raw array
&& !std::is_array_v<std::remove_cv_t<T>>
&& requires (T cont) {
// data(cont) is well-formed and has a valid type
{ data(cont); } -> ValidForElement<E>
// size(cont) is well-formed
{ size(cont); }
};
你会使用哪个:
template <typename Element, std::ptrdiff_t Extent = -1>
struct span {
template <typename C> requires AllowedContainer<C, Element>
span(C&);
template <typename C> requires AllowedContainer<C const, Element>
span(C const&);
};
那里的 const
-ness 要求阻止了很好的 partial-concept-id 语法,但我想我们可以为此添加另一个概念:
template <typename T, typename E>
concept ConstAllowedContainer = AllowedContainer<T const, E>;
template <typename Element, std::ptrdiff_t Extent = -1>
struct span {
template <AllowedContainer<E> C> span(C&);
template <ConstAllowedContainer<E> C> span(C const&);
};
不确定这里是否有更聪明的方法。
但实际上这整对构造函数可能是个错误,您想做一个转发参考:
template <typename Element, std::ptrdiff_t Extent = -1>
struct span {
template <AllowedContainer<E> C>
span(C&&);
};
最后一种方法需要对概念进行一些调整(所有 remove_cv_t
都应该变成 remove_cvref_t
)。
你误会了概念。在概念世界中,使用 concept-ts 语法的 span 构造函数将如下所示:
struct span{
span(const ContiguousRange&);
span(ContiguousRange&);
span(const span&) =default;
span(span&) =default;
};
或使用 c++20:
struct span{
span(const ContiguousRange auto&);
span(ContiguousRange auto&);
span(const span&) =default;
span(span&) =default;
};
这里的概念是为了简化抽象。因此,如果您的界面在使用概念时变得更加复杂,那么您就错过了抽象。这里的抽象是ContiguousRange
(感谢@Lyberta)。
我正在使用 Concepts TS 实现我自己的 std::span
版本。我在执行 these constructors:
template<class Container> constexpr span(Container& cont);
template<class Container> constexpr span(const Container& cont);
备注:这些构造函数不应参与重载决策,除非:
Container
不是span
和 的特化
Container
不是array
的特化
如何使用概念来实现?
您可以使用类型特征来检查某个类型是否是 span
或 std::array
的特化。这对我有用:
#include <type_traits>
template<typename, std::ptrdiff_t> class span;
template <typename T>
struct is_array : std::false_type { };
template <typename T, size_t N>
struct is_array<std::array<T, N>> : std::true_type { };
template <typename T>
struct is_span : std::false_type { };
template <typename T, std::ptrdiff_t P>
struct is_span<span<T, P>> : std::true_type { };
template <typename T>
concept bool NotSpanNotArray = !is_array<T>::value && !is_span<T>::value;
template<typename, std::ptrdiff_t> class span {
public:
template<NotSpanNotArray T> constexpr span(T& cont);
// template<NotSpanNotArray T> constexpr span(const T& cont);
};
工作演示:https://wandbox.org/permlink/M0n60U8Hl4mpacuI
只是我不是100%确定这样的方案是否满足参与重载决议当且仅当的要求。一些语言律师可能会澄清这一点。
更新
我刚刚意识到 std::is_array
仅适用于 "ordinary" 数组,不适用于 std::array
。因此我也添加了一个自定义 is_array
类型特征。
首先,创建特征以检查专业化。 array
和 span
在接受类型参数和非类型参数的意义上看起来相同:
template <typename T, template <typename, auto> class Z>
struct is_specialization : std::false_type { };
template <typename A, auto V, template <typename, auto> class Z>
struct is_specialization<Z<A,V>, Z> : std::true_type { };
template <typename T, template <typename, auto> class Z>
inline constexpr bool is_specialization_v = is_specialization<T, Z>::value;
然后我们可以从中建立一个概念:
// the last bullet point
template <typename T, typename E>
concept ValidForElement =
ConvertibleTo<std::remove_pointer_t<T>(*)[], E(*)[]>;
template <typename T, typename E>
concept AllowedContainer =
// not a specialization of span (note: requires forward declaration of span)
!is_specialization_v<std::remove_cv_t<T>, std::span>
// not a specialization of array
&& !is_specialization_v<std::remove_cv_t<T>, std::array>
// not a raw array
&& !std::is_array_v<std::remove_cv_t<T>>
&& requires (T cont) {
// data(cont) is well-formed and has a valid type
{ data(cont); } -> ValidForElement<E>
// size(cont) is well-formed
{ size(cont); }
};
你会使用哪个:
template <typename Element, std::ptrdiff_t Extent = -1>
struct span {
template <typename C> requires AllowedContainer<C, Element>
span(C&);
template <typename C> requires AllowedContainer<C const, Element>
span(C const&);
};
那里的 const
-ness 要求阻止了很好的 partial-concept-id 语法,但我想我们可以为此添加另一个概念:
template <typename T, typename E>
concept ConstAllowedContainer = AllowedContainer<T const, E>;
template <typename Element, std::ptrdiff_t Extent = -1>
struct span {
template <AllowedContainer<E> C> span(C&);
template <ConstAllowedContainer<E> C> span(C const&);
};
不确定这里是否有更聪明的方法。
但实际上这整对构造函数可能是个错误,您想做一个转发参考:
template <typename Element, std::ptrdiff_t Extent = -1>
struct span {
template <AllowedContainer<E> C>
span(C&&);
};
最后一种方法需要对概念进行一些调整(所有 remove_cv_t
都应该变成 remove_cvref_t
)。
你误会了概念。在概念世界中,使用 concept-ts 语法的 span 构造函数将如下所示:
struct span{
span(const ContiguousRange&);
span(ContiguousRange&);
span(const span&) =default;
span(span&) =default;
};
或使用 c++20:
struct span{
span(const ContiguousRange auto&);
span(ContiguousRange auto&);
span(const span&) =default;
span(span&) =default;
};
这里的概念是为了简化抽象。因此,如果您的界面在使用概念时变得更加复杂,那么您就错过了抽象。这里的抽象是ContiguousRange
(感谢@Lyberta)。