Java方法引用的C++等价物?

nnsrf1az  于 11个月前  发布在  Java
关注(0)|答案(3)|浏览(91)

我有一个std::vector<std::vector<int>>,我想检查是否至少有一个子向量是空的。所以我有这个:

std::any_of(vec.begin(), vec.end(), [](const auto& subvec)  {
     return subvec.empty();
});

字符串
C++14中有这样的东西吗?

std::any_of(vec.begin(), vec.end(), std::vector<int>::empty);


我尝试了上面的语法,但不起作用,在方法前面加上一个&也不起作用。

c8ib6hqw

c8ib6hqw1#

在评论中,你说:
没有什么不好的,除了如果确实有一个更短和(可以说)更可读的方式来写它,
所以你真正想要的是使用命名函数(一个很好的编程实践)。我们可以这样做,仍然使用lambda。只需创建一个命名lambda。

#include <algorithm>

auto isEmpty = [](std::vector<int> const& v)  {return v.empty(); /* or std::empty(v) */};

int main()
{
    std::vector<std::vector<int>>   vec;

    auto f = std::any_of(std::begin(vec), std::end(vec), isEmpty);
}

字符串

hrysbysz

hrysbysz2#

使用lambda是可读的方式。
仅用于说明,让我们探索其他方法。 predicate p需要(从cppreference):
对于v类型的每个参数,表达式p(v)必须可转换为bool(可能是const)VT,其中VTInputIt的值类型,无论值类别如何,都不能修改v。因此,不允许VT&的参数类型,VT也不是,除非对于VT,移动等价于复制(C++11起)。
除了隐式转换之外,这基本上意味着 predicate 必须是具有签名bool (const std::vector<int>&)的可调用。
std::function是一个候选者。它带来了相当大的开销,因为它的主要目的是类型擦除。我们不需要类型擦除,但std::function也有一种机制,可以将成员函数指针转换为可调用对象,其中对象被传递给函数。即,我们可以将&std::vector<int>::empty,一个成员函数指针,转换为带有签名bool (const std::vector<int>&)的东西。到目前为止一切顺利,但是这种转换不是隐式的,并且不能很好地与类模板参数演绎一起使用。因此,语法相当笨拙:

int main () {
    std::vector<std::vector<int>> vec;
    std::any_of(vec.begin(),vec.end(),std::function<bool(const std::vector<int>&)>(&std::vector<int>::empty));
}

字符串
Live Demo
嗯.我们可以把成员函数指针变成一个带有右签名的可调用对象,但是std::function并不是这里真正需要的,它对简洁的语法没有帮助。写一个自定义的 Package 器怎么样:

template <typename T> struct class_type;
template <typename C> struct class_type<bool(C::*)() const noexcept> { using type = C;};

template <auto F>
struct bool_member_to_functor {    
    using type = typename class_type<std::decay_t<decltype(F)>>::type;
    bool operator()(const type& c) {
        return (c.*F)();
    }
};

int main (int argc, char** argv)
{
    std::vector<std::vector<int>> vec;
    std::any_of(vec.begin(),vec.end(),bool_member_to_functor<&std::vector<int>::empty>{});
}


这导致调用的语法很好。直接传递成员函数是不可能的,但这是最接近的。然而,class_type在这里有点欺骗。你需要更多的专门化来覆盖所有的const/ non-const,而不是noexcept等变体。当成员函数被重载时,情况会变得更糟。这不是你真正想写的东西。
Live Demo
结论:lambda表达式是 Package 成员函数的轻量级、易读的方法。我怀疑它能变得更易读。

3hvapo4f

3hvapo4f3#

问题是C++中灵活的重载规则和缺乏重载集管理的语义。成员函数可以通过CV以及其对象的引用(右值/左值)限定来重载:

struct foo{
    void bar()const &;
    void bar()&&

    void bz();
};

字符串
现在表达式&foo::bar变得模糊了:有一个重载带有const左值对象,还有一个重载带有右值可变对象。消除表达式模糊的唯一方法是:
1.显式转换:

static_cast<void (foo::*) const &()>(&foo::bar)


1.或者绑定到调用站点的对象,它只能被 Package 为lambda。
但是如果函数没有重载,可以通过获取其地址来创建函数指针:&foo::bz工作没有任何问题。
为了解决上述问题,由Eric Neibler开创的<ranges>库引入了Neibloid模式。这个想法可以在任何非成员函数中实现。上面的链接中提供的文章并不是直接回答你的问题。但它是由C++社区的一个超级活跃的成员撰写的,向你展示了这种语言面临的许多挑战和技术细节。

相关问题