C++ 将函数对象作为左值 and/or 右值传递

C++ Passing function objects as lvalues and/or rvalues

我有一个 class 应该根据用户提供的谓词过滤其内容。我得到的接口规定引用谓词:

class Test {
  vector<int> data;
public:
  template <class PREDTYPE>
  void filter(PREDTYPE& pred) {
    return;
  }
};

我也给了一段测试代码,大致是这样的:

class Filter {
public:
  bool operator()(int) const {
    return false;
  }
};

int main() {
  Test test;
  test.filter(Filter());
}

这不编译,说cannot bind non-const lvalue reference of type 'Filter&' to an rvalue of type 'Filter'。如果我将测试代码更改为

int main() {
  Test test;
  Filter filter;
  test.filter(filter);
}

它有效,但这取决于最终用户,我无法控制他们的行为。我尝试重载 filter 方法,创建一个版本,该版本将按值接受谓词,然后通过引用传递它,但这也无法编译,并显示消息 call of overloaded 'filter(Filter&)' is ambiguous.

因此我的问题是:是否可以构造一个过滤器来接受谓词的右值和左值?

您可能希望将您的成员函数签名更改为

template <class PREDTYPE> void filter(PREDTYPE&& pred)

此处,谓词作为 forwarding reference 传递,它折叠为左值引用,左值引用传入右值,右值引用传入右值。

简而言之,是的,它是 (C++11)

您只需要依靠引用折叠规则来确保这一点:

template <typename PREDTYPE>
void filter(PREDTYPE&& pred) { // notice the &&
    // ... Whatever
    // Use perfect forwarding *IF* you can
    std::forward<PREDTYPE>(pred)(some_stuff);
    // Do not use pred after it has been forwarded!
}

这将接受右值和左值,而不必依赖 const- 引用(因此您的谓词仍然可以是可变的)。如果您坚持使用旧的 C++ 标准,最好的办法是改用 const 引用(上面突出显示警告)或将您的过滤器嵌入 std::function 并依赖调用站点的隐式转换。

通常,函数对象应该按值传递。所以不要超载并将您的签名更改为

template <class PREDTYPE> void filter(PREDTYPE pred)

可以找到为什么这样更好的解释 here

这取决于函数对谓词的作用。如果它在 returns 之后保留 reference/pointer,则您必须提供足够长的生命周期。但是,如果它不保留传入的谓词对象的任何内存,您可以将谓词转换为左值:

test.filter(static_cast<Filter&>(Filter()));

或者用一个简短的实用函数包装该转换:

template <class T>
T& stay(T&& a)
{ return a; }

test.filter(stay(Filter()));