Home > Mobile >  std::initializer_list as right hand argument for overloaded operator?
std::initializer_list as right hand argument for overloaded operator?

Time:09-23

I want to use something like the "in" operator available in other programming languages. I read many posts about that already. But, nothing that fits my needs.

What I wanted to do is a little bit different. Please see the following first example:

#include <iostream>
#include <initializer_list>
#include <algorithm>

bool operator ==(const int lhs, std::initializer_list<int>& il) {
    return std::find(il.begin(), il.end(), lhs) != il.end();
}
int main() {
    
    std::initializer_list<int> il{1,2,3,4,5};
    std::cout << (3 == il) << '\n';
    
    // std::cout << (3 == {1,2,3,4,5}) << '\n';   // Does not compile
}

But this does not compile. Presumably, because an initalizer list is no expression. Although there are exceptions. A std::initializer_list maybe a function parameter, although also here expressions are expected.

And since any operator is basically also a function, my hope was, that I also can use std::initalizer_list as argument.

But I cannot.

I tried the same approach by defining an own operator with a name by misusing overloading of 2 operators. See below:

#include <iostream>
#include <vector>

// New operator: is_in
enum { is_in };

int operator < (const int& lhs, decltype(is_in)) { return lhs; }
int operator > (int lhs, std::vector<int>& rhs) { return std::find(rhs.begin(), rhs.end(), lhs) != rhs.end();}
int operator > (int lhs, std::initializer_list<int>& rhs) { return std::find(rhs.begin(), rhs.end(), lhs) != rhs.end(); }

int main() {
    std::vector validValues{ 1, 2, 3, 4, 5 };

    bool b = (5 <is_in> validValues);

    // bool b = (5 <is_in> { 1, 2, 3, 4, 5 });  // Does not compile

    std::cout << b << '\n';
}

Same principle, same problem . . .

Is there any way to make this happen?

CodePudding user response:

Brace init lists are only allowed in certain contexts by exceptions to the grammar. And while an overloaded operator involves a function call, it must still obey the standard grammar rules unless you invoke it by name (operator@) in a function call.

So your named operator can work, but you must overload one of the operators for which an exception exists1. A rough summary of those overloadable operators is:

  • Assignment, including compound assignment (operator=, operator =).
  • Index (operator[]).
  • Function call (operator()).

None of those may be overloaded as a non-member, so getting symmetry (<is_in> or =is_in=), is not possible. But if forego that, we can do something like

#include <iostream>
#include <initializer_list>

inline constexpr class op {
    template<typename T>
    friend auto operator< (T const& t, op) {
        struct {
            T const & t;
            bool operator=(std::initializer_list<T> il) {
                return std::find(il.begin(), il.end(), t) != il.end();
            }
        } ret{t};
        return ret;
    }
} is_in;

int main() {

    bool b = (5 <is_in= { 1, 2, 3, 4, 5 });

    std::cout << b << '\n';
}

Which is not too pretty... I guess we can choose another operator instead of operator< to perhaps improve the look of this, but it seems like folly in general. This is already a bit too obscure.


1 - On that front, you should know that overloading operators whose parameters are purely standard types is ill-advised. The standard can change the library definition at any time and break your code gloriously.

CodePudding user response:

You need to take the initializer_list by const&:

bool operator==(const int lhs, const std::initializer_list<int>& il)
std::cout << (3 == std::initializer_list{1,2,3,4,5}) << '\n';

For the is_in test you could overload the comma operator and do something like this:

template<class T>
struct is_in {
    is_in(const std::initializer_list<T>& il) : ref(il) {}
    const std::initializer_list<T>& ref;
};

template<class T>
bool operator,(const T& lhs, const is_in<T>& rhs) {
    return std::find(rhs.ref.begin(), rhs.ref.end(), lhs) != rhs.ref.end();
}

int main() {
    bool b = (5, is_in{ 1, 2, 3, 4, 5 });

    std::cout << b << '\n';
}
  • Related