可迭代容器的 C++20 概念是什么?

时间:2021-02-22 22:52:36

标签: c++ templates c++17 c++20 typetraits

我仍在使用 C++17。我试图编写一个通用代码,从任何可迭代容器中检索“top count”元素。但在我看来,这看起来有些笨拙。

有没有更好的方法来用概念检查 C++20 中的类型特征?那会是什么样子?

要求可以从基于范围的 for 中获取,如 cppreference 所示,并且将是:

    auto && __range = range_expression ;
    auto __begin = begin_expr ;
    auto __end = end_expr ;
    for ( ; __begin != __end; ++__begin) {

        range_declaration = *__begin;
        loop_statement
    }

所以,beginend++*!=


我通过使用 SFINAE 机制定义类型特征构建了一个解决方案。请参阅:

#include <iostream>
#include <utility>
#include <unordered_map>
#include <queue>
#include <vector>
#include <iterator>
#include <type_traits>
#include <string>


// Helper for type trait We want to identify an iterable container ----------------------------------------------------
template <typename Container>
auto isIterableHelper(int) -> decltype (
    std::begin(std::declval<Container&>()) != std::end(std::declval<Container&>()),     // begin/end and operator !=
    ++std::declval<decltype(std::begin(std::declval<Container&>()))&>(),                // operator ++
    void(*std::begin(std::declval<Container&>())),                                      // operator*
    void(),                                                                             // Handle potential operator ,
    std::true_type{});
template <typename T>
std::false_type isIterableHelper(...);

// The type trait -----------------------------------------------------------------------------------------------------
template <typename Container>
using is_iterable = decltype(isIterableHelper<Container>(0));

// Some Alias names for later easier reading --------------------------------------------------------------------------
template <typename Container>
using ValueType = std::decay_t<decltype(*std::begin(std::declval<Container&>()))>;
template <typename Container>
using Pair = std::pair<ValueType<Container>, size_t>;
template <typename Container>
using Counter = std::unordered_map<ValueType<Container>, size_t>;
template <typename Container>
using UnderlyingContainer = std::vector<Pair<Container>>;

// Predicate Functor
template <class Container> struct LessForSecondOfPair {
    bool operator () (const Pair<Container>& p1, const Pair<Container>& p2) { return p1.second < p2.second; }
};
template <typename Container>
using MaxHeap = std::priority_queue<Pair<Container>, UnderlyingContainer<Container>, LessForSecondOfPair<Container>>;


// Function to get most frequent used number in any Container ---------------------------------------------------------
template <class Container>
auto topFrequent(const Container& data) {

    if constexpr (is_iterable<Container>::value) {

        // Count all occurences of data
        Counter<Container> counter{};
        for (const auto& d : data) counter[d]++;

        // Build a Max-Heap
        MaxHeap<Container> maxHeap(counter.begin(), counter.end());

        // Return most frequent number
        return maxHeap.top().first;
    }
    else
        return data;
}
// Test
int main() {
    std::vector testVector{ 1,2,2,3,3,3,4,4,4,4,5,5,5,5,6,6,6,6,6,7 };
    std::cout << "Most frequent is: " << topFrequent(testVector) << "\n";

    double cStyleArray[] = { 1.1, 2.2, 2.2, 3.3, 3.3, 3.3 };
    std::cout << "Most frequent is: " << topFrequent(cStyleArray) << "\n";

    std::string s{ "abbcccddddeeeeeffffffggggggg" };
    std::cout << "Most frequent is: " << topFrequent(s) << "\n";

    double value = 12.34;
    std::cout << "Most frequent is: " << topFrequent(value) << "\n";

    return 0;
}

那么,C++20 的解决方案是什么?


使用 Microsoft Visual Studio Community 2019 版本 16.8.2 进行开发和测试。

使用带有标志 --std=c++17 -Wall -Wextra -Wpedantic

的 clang11.0 和 gcc10.2 额外编译和测试

语言:C++17

1 个答案:

答案 0 :(得分:1)

您的 ValueType 特征基本上是 std::ranges::range_value_t,而您的 is_iterable 基本上是 concept std::ranges::range

template <typename Container>
using ValueType = std::ranges::range_value_t<Container>;

template <class Container>
auto topFrequent(const Container& data) {
  if constexpr (std::ranges::range<Container>) {
    // Count all occurences of data
    Counter<Container> counter{};
    for (const auto& d : data) counter[d]++;

    // Build a Max-Heap
    MaxHeap<Container> maxHeap(counter.begin(), counter.end());

    // Return most frequent number
    return maxHeap.top().first;
  }
  else
    return data;
}

See Demo.