C ++将函数对象作为左值和/或右值传递

时间:2018-07-05 09:20:58

标签: c++ templates function-object

我有一个应根据用户提供的谓词过滤其内容的类。给我的接口规定了对谓词的引用:

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

因此,我的问题是:是否有可能构造一个既可以接受谓词的rvalue又可以接受lvalue的过滤器?

4 个答案:

答案 0 :(得分:4)

简而言之,是(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!
}

这将接受rvalue和lvalues,而不必依赖const-引用(因此,您的谓词仍然是可变的)。如果您坚持使用较旧的C ++标准,则最好的选择是改用const引用(上面强调了警告)或将过滤器嵌入std::function中,并依靠调用站点的隐式转换。

答案 1 :(得分:2)

这取决于函数对谓词的作用。如果它在返回后仍保留了对它的引用/指针,则您必须提供可以长期使用的东西。但是,如果它不保留传入的谓词对象的任何内存,则可以将谓词转换为左值:

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

或将其转换为简短的实用函数:

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

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

答案 2 :(得分:1)

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

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

在此,谓词作为通用引用传递,对于在传入的rvalue的右值引用中传递的左值,折叠为左值引用。

答案 3 :(得分:0)

通常,函数对象应按值传递。因此,请勿超载并将签名更改为

template <class PREDTYPE> void filter(PREDTYPE pred)

可以找到更好的解释here