如何为标准库容器定义C++概念?

7
我希望能够为标准库容器实现一个C++概念。
非常感谢您提前的帮助!
4个回答

7

使用C++概念库修复了Caleth的答案:

template <class ContainerType> 
concept Container = requires(ContainerType a, const ContainerType b) 
{
    requires std::regular<ContainerType>;
    requires std::swappable<ContainerType>;
    requires std::destructible<typename ContainerType::value_type>;
    requires std::same_as<typename ContainerType::reference, typename ContainerType::value_type &>;
    requires std::same_as<typename ContainerType::const_reference, const typename ContainerType::value_type &>;
    requires std::forward_iterator<typename ContainerType::iterator>;
    requires std::forward_iterator<typename ContainerType::const_iterator>;
    requires std::signed_integral<typename ContainerType::difference_type>;
    requires std::same_as<typename ContainerType::difference_type, typename std::iterator_traits<typename
ContainerType::iterator>::difference_type>;
    requires std::same_as<typename ContainerType::difference_type, typename std::iterator_traits<typename
ContainerType::const_iterator>::difference_type>;
    { a.begin() } -> std::same_as<typename ContainerType::iterator>;
    { a.end() } -> std::same_as<typename ContainerType::iterator>;
    { b.begin() } -> std::same_as<typename ContainerType::const_iterator>;
    { b.end() } -> std::same_as<typename ContainerType::const_iterator>;
    { a.cbegin() } -> std::same_as<typename ContainerType::const_iterator>;
    { a.cend() } -> std::same_as<typename ContainerType::const_iterator>;
    { a.size() } -> std::same_as<typename ContainerType::size_type>;
    { a.max_size() } -> std::same_as<typename ContainerType::size_type>;
    { a.empty() } -> std::same_as<bool>;
};

1
我猜在iterator_traits相关部分,ContainerType::iterator周围不应该有下划线,或者我漏掉了什么? - alagner
4
语法{ expression } -> typename <TYPE> 似乎无法编译。它可能是在概念标准化之前被删除的特性吗? - BeeOnRope
@BeeOnRope同意。这段代码不符合C++20的规范。 - undefined

6

有“容器”概念的前置需求,这个概念大致如下:

template <class E>
concept default_erasable = requires(E * p) {
    std::destroy_at(p);
};

template <class E, class T, class A>
concept allocator_erasable = requires(A m, E * p) {
    requires std::same_as<typename T::allocator_type, typename std::allocator_traits<A>::rebind_alloc<E>>;
    std::allocator_traits<A>::destroy(m, p);
};

template <class T>
concept allocator_aware = requires (T a) {
    { a.get_allocator() } -> std::same_as<typename T::allocator_type>;
};

template <class T>
struct is_basic_string : std::false_type {};

template <class C, class T, class A>
struct is_basic_string<std::basic_string<C, T, A>> : std::true_type {};

template <class T>
constexpr bool is_basic_string_v = is_basic_string<T>::value;

template <class E, class T>
concept erasable = (is_basic_string_v<T> && default_erasable<E>)
                || (allocator_aware<T> && allocator_erasable<E, T, typename T::allocator_type>) 
                || (!allocator_aware<T> && default_erasable<E>);

template <class T>
concept container = requires(T a, const T b)
{
    requires std::regular<T>;
    requires std::swappable<T>;
    requires erasable<typename T::value_type, T>;
    requires std::same_as<typename T::reference, typename T::value_type &>;
    requires std::same_as<typename T::const_reference, const typename T::value_type &>;
    requires std::forward_iterator<typename T::iterator>;
    requires std::forward_iterator<typename T::const_iterator>;
    requires std::signed_integral<typename T::difference_type>;
    requires std::same_as<typename T::difference_type, typename std::iterator_traits<typename T::iterator>::difference_type>;
    requires std::same_as<typename T::difference_type, typename std::iterator_traits<typename T::const_iterator>::difference_type>;
    { a.begin() } -> std::same_as<typename T::iterator>;
    { a.end() } -> std::same_as<typename T::iterator>;
    { b.begin() } -> std::same_as<typename T::const_iterator>;
    { b.end() } -> std::same_as<typename T::const_iterator>;
    { a.cbegin() } -> std::same_as<typename T::const_iterator>;
    { a.cend() } -> std::same_as<typename T::const_iterator>;
    { a.size() } -> std::same_as<typename T::size_type>;
    { a.max_size() } -> std::same_as<typename T::size_type>;
    { a.empty() } -> std::convertible_to<bool>;
};

您可能希望添加requires std::ranges::range<T>;以进行概念部分排序。

在coliru上查看


4
好的,谢谢!我觉得这个似乎是非常普遍的东西,不知道为什么它不是标准的一部分... - user176168
s/boolean/bool/ - Steve Ward
@SteveWard 它只需要能够转换为 bool。我认为有一个概念可以做到这一点,但它仅供阐述 - Caleth

4

修正后的版本可以在C++中编译:

template <class ContainerType>
concept Container = requires(ContainerType a, const ContainerType b)
{
    requires std::regular<ContainerType>;
    requires std::swappable<ContainerType>;
    requires std::destructible<typename ContainerType::value_type>;
    requires std::same_as<typename ContainerType::reference, typename ContainerType::value_type &>;
    requires std::same_as<typename ContainerType::const_reference, const typename ContainerType::value_type &>;
    requires std::forward_iterator<typename ContainerType::iterator>;
    requires std::forward_iterator<typename ContainerType::const_iterator>;
    requires std::signed_integral<typename ContainerType::difference_type>;
    requires std::same_as<typename ContainerType::difference_type, typename std::iterator_traits<typename ContainerType::iterator>::difference_type>;
    requires std::same_as<typename ContainerType::difference_type, typename std::iterator_traits<typename ContainerType::const_iterator>::difference_type>;
    { a.begin() } -> std::same_as<typename ContainerType::iterator>;
    { a.end() } -> std::same_as<typename ContainerType::iterator>;
    { b.begin() } -> std::same_as<typename ContainerType::const_iterator>;
    { b.end() } -> std::same_as<typename ContainerType::const_iterator>;
    { a.cbegin() } -> std::same_as<typename ContainerType::const_iterator>;
    { a.cend() } -> std::same_as<typename ContainerType::const_iterator>;
    { a.size() } -> std::same_as<typename ContainerType::size_type>;
    { a.max_size() } -> std::same_as<typename ContainerType::size_type>;
    { a.empty() } -> std::same_as<bool>;
};
static_assert(Container<std::vector<unsigned char>>);
static_assert(Container<std::string>);

那个答案和其他答案有什么不同?如果其他回答中有错误,为什么不在它们上面加注释呢? - Etienne de Martel
1
@EtiennedeMartel 其他答案无法编译,而这个可以。 - Steve Ward
1
std::destructible<typename ContainerType::value_type> isn't the same as Erasable, empty is not required to be exactly bool - Caleth

1
如果OP感兴趣,可以了解范围的概念(例如类似容器的对象)。
在这里找到:https://en.cppreference.com/w/cpp/ranges 如果链接不可接受,快速谷歌搜索“C++范围”将带您到cppreference.com的页面。
您可以像我一样做这样的事情:
void a_function(std::ranges::forward_range auto container)
{
...
}

或者另一种选择是...

template<typename T>
concept YourContainer = requires(T container)
{
    requires std::ranges::range<T>;
    ...
};

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接