1

I have code that uses fold expressions to compare function argument against integer parmeters of class template. Code works AFAIK, but I wonder if it is possible to do what I want without _impl helper function.

Full code(my question is if contains can be implemented without contains_impl):

#include <algorithm>
#include <iostream>
#include <utility>
#include <cstdlib>
#include <tuple>

template <int H, int... T>
class if_set {
    private:
    template<typename... Ts>
    bool contains_impl(const int& val, Ts... ts) const{
        return (false || ... || (val == ts));
    }    
    public:
    bool contains(const int& val) const {
        return contains_impl( val, H, T...);
    }
};

using namespace std;
int main()
{
    constexpr if_set<1,3,4,5> isi;
    for (int i = -1; i < 10; ++i) {
        cout << i << ": " << boolalpha << isi.contains(i) << endl;
    }
    if_set<'a','e','i','o','u', 'A', 'E', 'I', 'O', 'U'>  vowels;
    string word = "ARCADE FIRE: Modern man";
    cout << word << endl;
    word.erase(remove_if(word.begin(), word.end(), [&vowels](const char& c){return vowels.contains (c);}), word.end());
    cout << word << endl;
}

Note 1: I know this code has many issues, I do not plan to use it in production and I discourage people from using it directly or as inspiration, this is a toy example I wanted to implement after reading interesting article about frozen C++ library.

Note 2: false || looks ugly, but IDK any nicer way.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
NoSenseEtAl
  • 28,205
  • 28
  • 128
  • 277
  • Why not `return (val == H) || (... || val == T);`? – Justin May 25 '17 at 20:15
  • because some weird error: prog.cc: In member function 'bool if_set::contains(const int&) const': prog.cc:17:38: error: operand of fold expression has no unexpanded parameter packs return (val == H) || (... || val == T); – NoSenseEtAl May 25 '17 at 20:16

1 Answers1

2

Yes, you can do it:

template <int H, int... T>
class if_set {
public:
    bool contains(const int& val) const {
        return ((val == H) || ... || (val == T));
    }
};

Alternatively, you could just work on std::integer_sequences:

template<typename T1, typename T2, T1... Is>
bool contains(std::integer_sequence<T1, Is...>, T2 val) {
    return (... || (val == Is)); // perhaps should be (false || ... || (val == Is)), but this appears to work
}
Justin
  • 24,288
  • 12
  • 92
  • 142
  • Ah, I needed extra () around (val == H) || ... || (val == T).. and error msg was super unhelpful... :/ Just another of fun C++ features. Do you know why () is required? – NoSenseEtAl May 25 '17 at 20:35
  • @NoSenseEtAl Because fold expressions are required to be surrounded with parentheses. `(... || Bools)` is valid whereas `... || Bools` is not. As for the reason, I'm guessing because it makes it easier to determine exactly where the fold expression is. – Justin May 25 '17 at 20:37
  • 1
    According to cppreference, the rule for the expression is "does not contain an operator with precedence lower than cast at the top level" A `==` is lower than a cast. So pretty much parentheses, but you should be able to do something like `!(val == T) && ...` or `f(val, T) || ...` or `good[T] || ...`, etc. Also, couldn't you do `template blah blah return ((val ==T) || ...);`? I.e., why have `H`? – TrentP May 26 '17 at 20:57