C++17 fold syntax to test vector composition

Viewed 214

Standard C++17 implementation of vector any, all:

template<class C, class T>
bool contains(const C& c, const T& value) {
  return std::find(c.begin(), c.end(), value) != c.end();
}

template<class C, class... T>
bool any(const C& c, T&&... value) {
  return (... || contains(c, value));
}

template<class C, class... T>
bool all(const C& c, T&&... value) {
  return (... && contains(c, value));
}

for usage as in

std::array<int, 6> data0 = { 4, 6, 8, 10, 12, 14 };
assert( any(data0, 10, 55, 792));
assert( !any(data0, 11));

assert( all(data0, 6, 14, 8));
assert( !all(data0, 6, 7, 8));

Is there an analogous way to define only, which returns true if and only if the set of unique values of the vector matches the input values? So the following asserts would hold

std::array<int, 6> data1 = { 1, 1, 2, 1, 2 };
assert( only(data1, 1, 2));
assert( !only(data1, 1));
3 Answers

You can provide a count function:

template<class C, class T>
auto count(const C& c, const T& value) {
  return std::count(c.begin(), c.end(), value);
}

and write only like this:

template<class C, class... T>
bool only(const C& c, T&&... value) {
  return (count(c, value) + ...) == c.size();
}

This takes care of duplicate elements in c, but requires the values to be unique.

Here's a demo.

template<class C, class...Ts>
bool only( C const& c, Ts&&...ts ) {
  std::size_t count = (std::size_t(0) + ... + contains(c, ts));
  return count == c.size();
}

this counts how many of the list ts... are in c, and returns true if the number you found ear equal to the elements of c. Now this assumes uniqueness of c and ts.

We just move the counting into only and test within the std algorithm:

template<class C, class...Ts>
bool only( C const& c, Ts&&...ts ) {
  using std::begin; using std::end;
  auto count = std::count_if( begin(c), end(c), [&](auto&& elem) {
    return ((elem == ts) || ...);
  } );
  return count == c.size();
}

and bob is your uncle.

We could also do a notcontains based only algorithm, but I think that is more complex.

It doesn't use fold expressions but it should work

template<class C, class... T>
bool only(const C& c, T&& ...vals) {
    auto ilist = {vals...}; //create initializer_list

    for (auto el : c) {
        if (!contains(ilist, el)) return false;
    }
    return true;
}

Something that uses fold expressions and not std::initializer_list

template<class T, class... Ts>
bool is_one_of(const T& val, Ts&& ...vals)
{
    return ((val == vals) || ...);
}

template<class C, class... Ts>
bool only(const C& c, Ts&& ...vals)
{
    for (const auto& el : c) {
        if (!is_one_of(el, vals...)) return false;
    }
    return true;
}

// or if you hate raw loops
template<class C, class... Ts>
bool only(const C& c, Ts&& ...vals)
{
    using std::beign; using std::end;
    auto it = std::find_if(begin(c), end(c), [&](const auto& el) {
        return !is_one_of(el, vals...);
    });
    return (it == end(c));
}
Related