重载运算符的右操作数可以使用std::initializer_list吗?

3

我想使用类似于其他编程语言中提供的“in”运算符。我已经阅读了许多相关帖子,但没有适合我的需求的。

我想要做的有点不同。请先看以下第一个示例:

#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
}

但这段代码无法编译。可能是因为初始化列表不是一个表达式。尽管也有例外。std :: initializer_list 可以作为函数参数,但是此处也需要表达式。
由于任何运算符基本上也是一个函数,我希望我也可以将 std :: initalizer_list 用作参数。
但我不能这样做。
我尝试通过定义自己的运算符并通过重载两个运算符的方法来使用该方法。请参见下面:
#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';
}

同样的原则,同样的问题......

有没有办法让这个实现呢?


关于初始化列表和运算符的右操作数的相关内容。 - Jarod42
@Jarod42:谢谢。看了那个,我只有很低或者没有希望得到解决方案…… - A M
别名可以缩短std::initializer_list,作为解决方法... - Jarod42
2个回答

4

在语法的例外情况下,大括号初始化列表只允许在特定的上下文中使用。虽然重载运算符涉及函数调用,但除非您通过名称(operator@)在函数调用中调用它,否则它仍必须遵守标准语法规则。

因此,您的命名运算符可以工作,但是您必须重载其中一个存在例外情况的运算符1。这些可重载运算符的粗略摘要如下:

  • 赋值,包括复合赋值(operator=, operator +=)。
  • 索引(operator[])。
  • 函数调用(operator())。

这些都不能作为非成员进行重载,因此无法获得对称性(<is_in>=is_in =)。但如果不考虑对称性,则可以像这样做

#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';
}

这看起来并不太美观...我想我们可以选择另一个运算符而不是 operator< 来改善这个问题,但总的来说似乎有些愚蠢。这已经有点太难懂了。


1 - 在这方面,你应该知道,重载参数仅为标准类型的运算符是不明智的。标准可以随时更改库定义并彻底破坏你的代码。


2
提高了解释。不过,is_in= { 对我来说仍然是不自然的。 - Const

3
你需要使用 const& 带入 initializer_list
bool operator==(const int lhs, const std::initializer_list<int>& il)

std::cout << (3 == std::initializer_list{1,2,3,4,5}) << '\n';

针对 is_in 测试,你可以重载逗号运算符并做如下操作:
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';
}

是的,正确的,谢谢。但我不想写“std::initializer_list{1,2,3,4,5}”,只想写“{1,2,3,4,5}”…… - A M
@ArminMontigny 我明白了。好的,那么这可能就没有什么帮助了。 - Ted Lyngmo

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