使用概念禁用课程专业化

时间:2019-01-16 16:53:35

标签: c++ c++-concepts c++20

我正在使用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的专业化

如何使用概念来实现这一点?

3 个答案:

答案 0 :(得分:3)

首先,创建一个特征以检查专业化。 arrayspan在使用类型参数和非类型参数的意义上看起来相同:

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)。

答案 1 :(得分:2)

您可以使用类型特征来检查某个类型是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仅适用于“普通”数组,不适用于std::array。因此,我还添加了自定义的is_array类型特征。

答案 2 :(得分:0)

您正在滥用概念。在概念世界中,构造器将使用concept-ts语法如下所示:

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)。